@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.
- package/dist/components/AccessibilityChecker.d.ts.map +1 -1
- package/dist/components/Html.d.ts +1 -1
- package/dist/components/Html.d.ts.map +1 -1
- package/dist/components/Logo.d.ts.map +1 -1
- package/dist/components/Markdown.d.ts +2 -2
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/SafeSpan.d.ts +1 -1
- package/dist/components/SafeSpan.d.ts.map +1 -1
- package/dist/components/base/ModelView.d.ts +1 -1
- package/dist/components/base/ModelView.d.ts.map +1 -1
- package/dist/components/blocks/Article.d.ts +1 -1
- package/dist/components/blocks/Article.d.ts.map +1 -1
- package/dist/components/blocks/CardListGrid.d.ts.map +1 -1
- package/dist/components/blocks/Code.d.ts.map +1 -1
- package/dist/components/blocks/Content.d.ts.map +1 -1
- package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -1
- package/dist/components/blocks/FeatureCard.d.ts.map +1 -1
- package/dist/components/blocks/FeatureGrid.d.ts.map +1 -1
- package/dist/components/blocks/Footer.d.ts.map +1 -1
- package/dist/components/blocks/Image.d.ts.map +1 -1
- package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
- package/dist/components/blocks/ProductCard.d.ts.map +1 -1
- package/dist/components/blocks/Section.d.ts.map +1 -1
- package/dist/components/blocks/Text.d.ts +8 -1
- package/dist/components/blocks/Text.d.ts.map +1 -1
- package/dist/components/buttons/Button.d.ts.map +1 -1
- package/dist/components/buttons/PaletteSwitcher.d.ts.map +1 -1
- package/dist/components/buttons/ThemeSwitcher.d.ts.map +1 -1
- package/dist/components/forms/FormBlock.d.ts +1 -1
- package/dist/components/forms/FormBlock.d.ts.map +1 -1
- package/dist/components/forms/SchemaFormRenderer.d.ts +28 -0
- package/dist/components/forms/SchemaFormRenderer.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +2 -0
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
- package/dist/components/input/HtmlInputField.d.ts.map +1 -1
- package/dist/components/layout/CollapsibleLayout/CollapsibleLayout.d.ts.map +1 -1
- package/dist/components/layout/GridLayout.d.ts +5 -0
- package/dist/components/layout/GridLayout.d.ts.map +1 -1
- package/dist/components/plugins/DataTable.d.ts +57 -0
- package/dist/components/plugins/DataTable.d.ts.map +1 -0
- package/dist/components/plugins/StatCard.d.ts +44 -0
- package/dist/components/plugins/StatCard.d.ts.map +1 -0
- package/dist/components/plugins/index.d.ts +13 -0
- package/dist/components/plugins/index.d.ts.map +1 -0
- package/dist/components/shared/createSerializableView.d.ts.map +1 -1
- package/dist/hooks/useBaseProps.d.ts +1161 -12
- package/dist/hooks/useBaseProps.d.ts.map +1 -1
- package/dist/index.esm.js +5468 -5216
- package/dist/index.js +5572 -5317
- package/dist/palettes/manifest.json +19 -19
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -1
- package/dist/utils/iconMap.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/components/AccessibilityChecker.tsx +10 -7
- package/src/components/ErrorBoundary.tsx +3 -3
- package/src/components/Html.tsx +17 -12
- package/src/components/Logo.tsx +1 -8
- package/src/components/Markdown.tsx +10 -10
- package/src/components/ResponsiveMenu.tsx +1 -1
- package/src/components/SafeSpan.tsx +9 -9
- package/src/components/Scaffold.tsx +4 -4
- package/src/components/base/ModelView.tsx +2 -2
- package/src/components/blocks/Article.tsx +7 -7
- package/src/components/blocks/CardListGrid.tsx +1 -3
- package/src/components/blocks/Code.tsx +10 -8
- package/src/components/blocks/Content.tsx +2 -4
- package/src/components/blocks/CoverImageHeader.tsx +3 -4
- package/src/components/blocks/FeatureCard.tsx +2 -4
- package/src/components/blocks/FeatureGrid.tsx +2 -4
- package/src/components/blocks/Footer.tsx +2 -4
- package/src/components/blocks/Image.tsx +8 -5
- package/src/components/blocks/PageBannerHeader.tsx +3 -4
- package/src/components/blocks/ProductCard.tsx +8 -5
- package/src/components/blocks/Section.tsx +6 -4
- package/src/components/blocks/Text.tsx +15 -7
- package/src/components/buttons/Button.tsx +8 -6
- package/src/components/buttons/PaletteSwitcher.tsx +6 -8
- package/src/components/buttons/ThemeSwitcher.tsx +8 -9
- package/src/components/forms/Captcha.tsx +1 -1
- package/src/components/forms/FormBlock.tsx +3 -5
- package/src/components/forms/FormCheckbox.tsx +1 -1
- package/src/components/forms/FormField.tsx +1 -1
- package/src/components/forms/FormSelect.tsx +1 -1
- package/src/components/forms/SchemaFormRenderer.tsx +268 -0
- package/src/components/forms/__tests__/SchemaFormRenderer.test.tsx +212 -0
- package/src/components/forms/index.ts +3 -0
- package/src/components/index.ts +1 -0
- package/src/components/input/ChoiceInputField.tsx +2 -1
- package/src/components/input/HtmlInputField.tsx +14 -9
- package/src/components/input/TextField.tsx +1 -1
- package/src/components/layout/CollapsibleLayout/CollapsibleLayout.tsx +6 -8
- package/src/components/layout/GridLayout.tsx +4 -0
- package/src/components/plugins/DataTable.tsx +259 -0
- package/src/components/plugins/StatCard.tsx +122 -0
- package/src/components/plugins/__tests__/DataTable.test.tsx +158 -0
- package/src/components/plugins/index.ts +14 -0
- package/src/components/shared/createSerializableView.tsx +8 -6
- package/src/hooks/useBaseProps.ts +1 -1
- package/src/schemas/transformers/ReactNodeTransformer.ts +13 -10
- package/src/utils/iconMap.tsx +143 -83
- package/dist/palettes/palette-autumn.1.4.9.css +0 -172
- package/dist/palettes/palette-autumn.1.4.9.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.0.css +0 -172
- package/dist/palettes/palette-autumn.1.5.0.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.1.css +0 -172
- package/dist/palettes/palette-autumn.1.5.1.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.2.css +0 -172
- package/dist/palettes/palette-autumn.1.5.2.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.4.css +0 -172
- package/dist/palettes/palette-autumn.1.5.4.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.5.css +0 -172
- package/dist/palettes/palette-autumn.1.5.5.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.6.css +0 -172
- package/dist/palettes/palette-autumn.1.5.6.min.css +0 -1
- package/dist/palettes/palette-autumn.1.5.7.css +0 -172
- package/dist/palettes/palette-autumn.1.5.7.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.4.9.css +0 -172
- package/dist/palettes/palette-cosmic.1.4.9.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.0.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.0.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.1.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.1.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.2.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.2.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.4.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.4.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.5.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.5.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.6.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.6.min.css +0 -1
- package/dist/palettes/palette-cosmic.1.5.7.css +0 -172
- package/dist/palettes/palette-cosmic.1.5.7.min.css +0 -1
- package/dist/palettes/palette-default.1.4.9.css +0 -178
- package/dist/palettes/palette-default.1.4.9.min.css +0 -1
- package/dist/palettes/palette-default.1.5.0.css +0 -178
- package/dist/palettes/palette-default.1.5.0.min.css +0 -1
- package/dist/palettes/palette-default.1.5.1.css +0 -178
- package/dist/palettes/palette-default.1.5.1.min.css +0 -1
- package/dist/palettes/palette-default.1.5.2.css +0 -178
- package/dist/palettes/palette-default.1.5.2.min.css +0 -1
- package/dist/palettes/palette-default.1.5.4.css +0 -178
- package/dist/palettes/palette-default.1.5.4.min.css +0 -1
- package/dist/palettes/palette-default.1.5.5.css +0 -178
- package/dist/palettes/palette-default.1.5.5.min.css +0 -1
- package/dist/palettes/palette-default.1.5.6.css +0 -178
- package/dist/palettes/palette-default.1.5.6.min.css +0 -1
- package/dist/palettes/palette-default.1.5.7.css +0 -178
- package/dist/palettes/palette-default.1.5.7.min.css +0 -1
- package/dist/palettes/palette-ocean.1.4.9.css +0 -172
- package/dist/palettes/palette-ocean.1.4.9.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.0.css +0 -172
- package/dist/palettes/palette-ocean.1.5.0.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.1.css +0 -172
- package/dist/palettes/palette-ocean.1.5.1.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.2.css +0 -172
- package/dist/palettes/palette-ocean.1.5.2.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.4.css +0 -172
- package/dist/palettes/palette-ocean.1.5.4.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.5.css +0 -172
- package/dist/palettes/palette-ocean.1.5.5.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.6.css +0 -172
- package/dist/palettes/palette-ocean.1.5.6.min.css +0 -1
- package/dist/palettes/palette-ocean.1.5.7.css +0 -172
- package/dist/palettes/palette-ocean.1.5.7.min.css +0 -1
- package/dist/palettes/palette-spring.1.4.9.css +0 -160
- package/dist/palettes/palette-spring.1.4.9.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.0.css +0 -160
- package/dist/palettes/palette-spring.1.5.0.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.1.css +0 -160
- package/dist/palettes/palette-spring.1.5.1.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.2.css +0 -160
- package/dist/palettes/palette-spring.1.5.2.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.4.css +0 -166
- package/dist/palettes/palette-spring.1.5.4.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.5.css +0 -166
- package/dist/palettes/palette-spring.1.5.5.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.6.css +0 -166
- package/dist/palettes/palette-spring.1.5.6.min.css +0 -1
- package/dist/palettes/palette-spring.1.5.7.css +0 -166
- package/dist/palettes/palette-spring.1.5.7.min.css +0 -1
- package/dist/palettes/palette-winter.1.4.9.css +0 -172
- package/dist/palettes/palette-winter.1.4.9.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.0.css +0 -172
- package/dist/palettes/palette-winter.1.5.0.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.1.css +0 -172
- package/dist/palettes/palette-winter.1.5.1.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.2.css +0 -172
- package/dist/palettes/palette-winter.1.5.2.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.4.css +0 -172
- package/dist/palettes/palette-winter.1.5.4.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.5.css +0 -172
- package/dist/palettes/palette-winter.1.5.5.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.6.css +0 -172
- package/dist/palettes/palette-winter.1.5.6.min.css +0 -1
- package/dist/palettes/palette-winter.1.5.7.css +0 -172
- package/dist/palettes/palette-winter.1.5.7.min.css +0 -1
- /package/dist/palettes/{palette-autumn.1.5.3.css → palette-autumn.1.5.8.css} +0 -0
- /package/dist/palettes/{palette-autumn.1.5.3.min.css → palette-autumn.1.5.8.min.css} +0 -0
- /package/dist/palettes/{palette-cosmic.1.5.3.css → palette-cosmic.1.5.8.css} +0 -0
- /package/dist/palettes/{palette-cosmic.1.5.3.min.css → palette-cosmic.1.5.8.min.css} +0 -0
- /package/dist/palettes/{palette-default.1.5.3.css → palette-default.1.5.8.css} +0 -0
- /package/dist/palettes/{palette-default.1.5.3.min.css → palette-default.1.5.8.min.css} +0 -0
- /package/dist/palettes/{palette-ocean.1.5.3.css → palette-ocean.1.5.8.css} +0 -0
- /package/dist/palettes/{palette-ocean.1.5.3.min.css → palette-ocean.1.5.8.min.css} +0 -0
- /package/dist/palettes/{palette-spring.1.5.3.css → palette-spring.1.5.8.css} +0 -0
- /package/dist/palettes/{palette-spring.1.5.3.min.css → palette-spring.1.5.8.min.css} +0 -0
- /package/dist/palettes/{palette-winter.1.5.3.css → palette-winter.1.5.8.css} +0 -0
- /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
|
|
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 (!
|
|
236
|
-
|
|
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 (!
|
|
241
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
173
|
-
|
|
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 (!
|
|
178
|
-
|
|
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 (!
|
|
137
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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 (!
|
|
150
|
-
|
|
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 (!
|
|
316
|
-
|
|
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 (!
|
|
321
|
-
|
|
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 (!
|
|
326
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
+
}
|