@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.
- package/README.md +1681 -2
- package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts +66 -0
- package/dist/__tests__/schemas/transformers/MockSerializableComponent.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.d.ts +7 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/Html.d.ts +28 -18
- package/dist/components/Html.d.ts.map +1 -1
- package/dist/components/Logo.d.ts +12 -35
- package/dist/components/Logo.d.ts.map +1 -1
- package/dist/components/Markdown.d.ts +18 -13
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/QwickApp.d.ts +16 -3
- package/dist/components/QwickApp.d.ts.map +1 -1
- package/dist/components/QwickIcon.d.ts +23 -0
- package/dist/components/QwickIcon.d.ts.map +1 -0
- package/dist/components/SafeSpan.d.ts +12 -5
- package/dist/components/SafeSpan.d.ts.map +1 -1
- package/dist/components/Scaffold.d.ts.map +1 -1
- package/dist/components/base/ModelView.d.ts +101 -0
- package/dist/components/base/ModelView.d.ts.map +1 -0
- package/dist/components/base/index.d.ts +11 -0
- package/dist/components/base/index.d.ts.map +1 -0
- package/dist/components/blocks/Article.d.ts +12 -2
- package/dist/components/blocks/Article.d.ts.map +1 -1
- package/dist/components/blocks/Code.d.ts +13 -2
- package/dist/components/blocks/Code.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/HeroBlock.d.ts +27 -13
- package/dist/components/blocks/HeroBlock.d.ts.map +1 -1
- package/dist/components/blocks/Image.d.ts +41 -0
- package/dist/components/blocks/Image.d.ts.map +1 -0
- package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -1
- package/dist/components/blocks/Section.d.ts +16 -2
- package/dist/components/blocks/Section.d.ts.map +1 -1
- package/dist/components/blocks/Text.d.ts +41 -0
- package/dist/components/blocks/Text.d.ts.map +1 -0
- package/dist/components/blocks/index.d.ts +4 -0
- package/dist/components/blocks/index.d.ts.map +1 -1
- package/dist/components/buttons/Button.d.ts +23 -7
- package/dist/components/buttons/Button.d.ts.map +1 -1
- package/dist/components/forms/FormBlock.d.ts +19 -13
- package/dist/components/forms/FormBlock.d.ts.map +1 -1
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/input/ChoiceInputField.d.ts +17 -11
- package/dist/components/input/ChoiceInputField.d.ts.map +1 -1
- package/dist/components/input/HtmlInputField.d.ts +17 -11
- package/dist/components/input/HtmlInputField.d.ts.map +1 -1
- package/dist/components/input/SelectInputField.d.ts +16 -10
- package/dist/components/input/SelectInputField.d.ts.map +1 -1
- package/dist/components/input/SwitchInputField.d.ts +16 -10
- package/dist/components/input/SwitchInputField.d.ts.map +1 -1
- package/dist/components/input/TextField.d.ts.map +1 -1
- package/dist/components/input/TextInputField.d.ts +16 -11
- package/dist/components/input/TextInputField.d.ts.map +1 -1
- package/dist/components/layout/GridCell.d.ts +23 -6
- package/dist/components/layout/GridCell.d.ts.map +1 -1
- package/dist/components/layout/GridLayout.d.ts +24 -23
- package/dist/components/layout/GridLayout.d.ts.map +1 -1
- package/dist/components/pages/FormPage.d.ts.map +1 -1
- package/dist/components/pages/Page.d.ts +49 -87
- package/dist/components/pages/Page.d.ts.map +1 -1
- package/dist/components/pages/index.d.ts +2 -2
- package/dist/components/pages/index.d.ts.map +1 -1
- package/dist/config/AppConfig.d.ts +49 -0
- package/dist/config/AppConfig.d.ts.map +1 -0
- package/dist/config/AppConfigBuilder.d.ts +75 -0
- package/dist/config/AppConfigBuilder.d.ts.map +1 -0
- package/dist/config/index.d.ts +13 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/types.d.ts +130 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.esm.js +451 -0
- package/dist/config.js +455 -0
- package/dist/contexts/PrintModeContext.d.ts +27 -0
- package/dist/contexts/PrintModeContext.d.ts.map +1 -0
- package/dist/contexts/QwickAppContext.d.ts +2 -2
- package/dist/contexts/QwickAppContext.d.ts.map +1 -1
- package/dist/contexts/index.d.ts +2 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/usePrintMode.d.ts +39 -0
- package/dist/hooks/usePrintMode.d.ts.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +10951 -6238
- package/dist/index.js +11014 -6287
- package/dist/schemas/CodeSchema.d.ts +2 -1
- package/dist/schemas/CodeSchema.d.ts.map +1 -1
- package/dist/schemas/CollapsibleLayoutSchema.d.ts +2 -1
- package/dist/schemas/CollapsibleLayoutSchema.d.ts.map +1 -1
- package/dist/schemas/ContentSchema.d.ts +2 -1
- package/dist/schemas/ContentSchema.d.ts.map +1 -1
- package/dist/schemas/GridCellSchema.d.ts +25 -0
- package/dist/schemas/GridCellSchema.d.ts.map +1 -0
- package/dist/schemas/GridLayoutSchema.d.ts +23 -0
- package/dist/schemas/GridLayoutSchema.d.ts.map +1 -0
- package/dist/schemas/HtmlSchema.d.ts +14 -0
- package/dist/schemas/HtmlSchema.d.ts.map +1 -0
- package/dist/schemas/ImageSchema.d.ts +32 -0
- package/dist/schemas/ImageSchema.d.ts.map +1 -0
- package/dist/schemas/LogoSchema.d.ts +35 -0
- package/dist/schemas/LogoSchema.d.ts.map +1 -0
- package/dist/schemas/MarkdownSchema.d.ts +14 -0
- package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
- package/dist/schemas/PageTemplateSchema.d.ts +31 -0
- package/dist/schemas/PageTemplateSchema.d.ts.map +1 -0
- package/dist/schemas/PrintConfigSchema.d.ts +31 -0
- package/dist/schemas/PrintConfigSchema.d.ts.map +1 -0
- package/dist/schemas/SectionSchema.d.ts +2 -1
- package/dist/schemas/SectionSchema.d.ts.map +1 -1
- package/dist/schemas/TextSchema.d.ts +37 -0
- package/dist/schemas/TextSchema.d.ts.map +1 -0
- package/dist/schemas/ViewModelSchema.d.ts +23 -0
- package/dist/schemas/ViewModelSchema.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +15 -1
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/transformers/ComponentTransformer.d.ts +116 -0
- package/dist/schemas/transformers/ComponentTransformer.d.ts.map +1 -0
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts +53 -0
- package/dist/schemas/transformers/ReactNodeTransformer.d.ts.map +1 -0
- package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts +66 -0
- package/dist/schemas/transformers/__tests__/MockSerializableComponent.d.ts.map +1 -0
- package/dist/schemas/transformers/registry.d.ts +15 -0
- package/dist/schemas/transformers/registry.d.ts.map +1 -0
- package/dist/schemas/types/Serializable.d.ts +46 -0
- package/dist/schemas/types/Serializable.d.ts.map +1 -0
- package/dist/utils/htmlTransform.d.ts.map +1 -1
- package/dist/utils/reactUtils.d.ts +12 -3
- package/dist/utils/reactUtils.d.ts.map +1 -1
- package/package.json +17 -3
- package/src/{components/__tests__ → __tests__/components}/AccessibilityProvider.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Article.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Breadcrumbs.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Button.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/CardListGrid.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/ChoiceInputField.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Code.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Content.integration.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Content.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/CoverImageHeader.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/ErrorBoundary.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/FeatureCard.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/FeatureGrid.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/FeatureGrid.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/Footer.test.tsx +4 -4
- package/src/{components/__tests__ → __tests__/components}/FormBlock.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/HeroBlock.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/HeroBlock.test.tsx +233 -7
- package/src/{components/__tests__ → __tests__/components}/Html.test.tsx +11 -2
- package/src/{components/__tests__ → __tests__/components}/HtmlInputField.test.tsx +3 -3
- package/src/__tests__/components/Logo.test.js +3 -3
- package/src/{components/__tests__ → __tests__/components}/Markdown.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/PageBannerHeader.test.tsx +3 -3
- package/src/{components/__tests__ → __tests__/components}/PaletteSwitcher.test.tsx +3 -3
- package/src/{components/__tests__ → __tests__/components}/ProductCard.test.tsx +4 -4
- package/src/{components/__tests__ → __tests__/components}/SafeSpan.integration.test.tsx +2 -2
- package/src/{components/__tests__ → __tests__/components}/SafeSpan.simple.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/SafeSpan.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Section.integration.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/Section.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/SelectInputField.test.tsx +1 -1
- package/src/{components/__tests__ → __tests__/components}/TextInputField.test.tsx +3 -3
- package/src/{components/__tests__ → __tests__/components}/ThemeSwitcher.test.tsx +3 -3
- package/src/__tests__/components/base/ModelView.test.tsx +220 -0
- package/src/__tests__/components/blocks/Code.performance.test.tsx +625 -0
- package/src/__tests__/components/blocks/Code.serialization.test.tsx +507 -0
- package/src/__tests__/components/blocks/HeroBlock.serialization.test.tsx +414 -0
- package/src/__tests__/components/blocks/Image.serialization.test.tsx +257 -0
- package/src/__tests__/components/blocks/Section.serialization.test.tsx +553 -0
- package/src/__tests__/components/blocks/Text.performance.test.tsx +442 -0
- package/src/__tests__/components/blocks/Text.serialization.test.tsx +491 -0
- package/src/__tests__/components/buttons/Button.serialization.test.tsx +443 -0
- package/src/__tests__/components/input/FormComponents.serialization.test.tsx +482 -0
- package/src/__tests__/components/input/SelectInputField.serialization.test.tsx +439 -0
- package/src/__tests__/components/input/TextInputField.serialization.test.tsx +359 -0
- package/src/{components/layout/CollapsibleLayout/__tests__ → __tests__/components/layout}/CollapsibleLayout.test.tsx +4 -4
- package/src/__tests__/components/layout/GridCell.serialization.test.tsx +403 -0
- package/src/__tests__/components/layout/GridLayout.serialization.test.tsx +311 -0
- package/src/__tests__/hooks/usePrintMode.test.ts +89 -0
- package/src/__tests__/schemas/PageTemplateSchema.test.ts +161 -0
- package/src/__tests__/schemas/PrintConfigSchema.test.ts +127 -0
- package/src/__tests__/schemas/ViewModelSchema.test.ts +80 -0
- package/src/__tests__/schemas/transformers/ComponentSerializationPatterns.test.tsx +602 -0
- package/src/__tests__/schemas/transformers/ComponentTransformer.htmlPatterns.test.ts +301 -0
- package/src/__tests__/schemas/transformers/ComponentTransformer.test.ts +521 -0
- package/src/__tests__/schemas/transformers/CrossBrowserCompatibility.test.ts +586 -0
- package/src/__tests__/schemas/transformers/MockSerializableComponent.ts +103 -0
- package/src/__tests__/schemas/transformers/RealWorldScenarios.test.tsx +1165 -0
- package/src/__tests__/schemas/transformers/SerializationErrorHandling.test.ts +602 -0
- package/src/__tests__/schemas/transformers/SerializationIntegration.test.tsx +691 -0
- package/src/__tests__/schemas/transformers/SerializationPerformance.test.ts +460 -0
- package/src/__tests__/schemas/transformers/TestAutomation.test.ts +597 -0
- package/src/{utils/__tests__ → __tests__/utils}/nested-dom-fix.test.tsx +1 -1
- package/src/components/ErrorBoundary.tsx +8 -8
- package/src/components/Html.tsx +147 -44
- package/src/components/Logo.tsx +198 -100
- package/src/components/Markdown.tsx +125 -16
- package/src/components/QwickApp.tsx +64 -31
- package/src/components/QwickIcon.tsx +59 -0
- package/src/components/SafeSpan.tsx +65 -10
- package/src/components/Scaffold.tsx +2 -8
- package/src/components/base/ModelView.tsx +199 -0
- package/src/components/base/index.ts +11 -0
- package/src/components/blocks/Article.tsx +57 -18
- package/src/components/blocks/Code.md +529 -0
- package/src/components/blocks/Code.tsx +102 -15
- package/src/components/blocks/CoverImageHeader.tsx +9 -4
- package/src/components/blocks/FeatureCard.tsx +1 -2
- package/src/components/blocks/FeatureGrid.tsx +19 -1
- package/src/components/blocks/Footer.tsx +13 -1
- package/src/components/blocks/HeroBlock.tsx +87 -20
- package/src/components/blocks/Image.tsx +395 -0
- package/src/components/blocks/PageBannerHeader.tsx +14 -12
- package/src/components/blocks/ProductCard.tsx +1 -1
- package/src/components/blocks/Section.tsx +113 -8
- package/src/components/blocks/Text.tsx +285 -0
- package/src/components/blocks/index.ts +4 -0
- package/src/components/buttons/Button.tsx +184 -15
- package/src/components/forms/FormBlock.tsx +70 -17
- package/src/components/index.ts +5 -0
- package/src/components/input/ChoiceInputField.tsx +48 -18
- package/src/components/input/HtmlInputField.tsx +48 -18
- package/src/components/input/SelectInputField.tsx +48 -16
- package/src/components/input/SwitchInputField.tsx +48 -17
- package/src/components/input/TextField.tsx +41 -1
- package/src/components/input/TextInputField.tsx +52 -18
- package/src/components/layout/GridCell.tsx +118 -9
- package/src/components/layout/GridLayout.tsx +125 -24
- package/src/components/pages/FormPage.tsx +0 -1
- package/src/components/pages/Page.css +304 -332
- package/src/components/pages/Page.tsx +307 -255
- package/src/components/pages/index.ts +2 -2
- package/src/config/AppConfig.ts +133 -0
- package/src/config/AppConfigBuilder.ts +421 -0
- package/src/config/__tests__/AppConfig.test.ts +385 -0
- package/src/config/__tests__/AppConfigBuilder.test.ts +432 -0
- package/src/config/index.ts +24 -0
- package/src/config/types.ts +170 -0
- package/src/config.ts +25 -0
- package/src/contexts/PrintModeContext.tsx +332 -0
- package/src/contexts/QwickAppContext.tsx +2 -2
- package/src/contexts/index.ts +2 -0
- package/src/hooks/index.ts +5 -1
- package/src/hooks/usePrintMode.ts +73 -0
- package/src/index.ts +3 -0
- package/src/schemas/CodeSchema.ts +3 -3
- package/src/schemas/CollapsibleLayoutSchema.ts +2 -1
- package/src/schemas/ContentSchema.ts +2 -1
- package/src/schemas/GridCellSchema.ts +164 -0
- package/src/schemas/GridLayoutSchema.ts +133 -0
- package/src/schemas/HtmlSchema.ts +47 -0
- package/src/schemas/ImageSchema.ts +235 -0
- package/src/schemas/LogoSchema.ts +241 -0
- package/src/schemas/MarkdownSchema.ts +47 -0
- package/src/schemas/PageTemplateSchema.ts +186 -0
- package/src/schemas/PrintConfigSchema.ts +207 -0
- package/src/schemas/README.md +661 -0
- package/src/schemas/SectionSchema.ts +2 -1
- package/src/schemas/TextSchema.ts +329 -0
- package/src/schemas/ViewModelSchema.ts +115 -0
- package/src/schemas/index.ts +21 -2
- package/src/schemas/transformers/ComponentTransformer.ts +403 -0
- package/src/schemas/transformers/ReactNodeTransformer.ts +236 -0
- package/src/schemas/transformers/registry.ts +72 -0
- package/src/schemas/types/Serializable.ts +51 -0
- package/src/stories/AccessibilityProvider.stories.tsx +253 -253
- package/src/stories/Article.stories.tsx +433 -433
- package/src/stories/Button.stories.tsx +1 -1
- package/src/stories/CardListGrid.stories.tsx +451 -451
- package/src/stories/ChoiceInputField.stories.tsx +503 -503
- package/src/stories/Code.stories.tsx +1 -1
- package/src/stories/CollapsibleLayout.stories.tsx +1414 -1414
- package/src/stories/Content.stories.tsx +393 -393
- package/src/stories/CoverImageHeader.stories.tsx +701 -701
- package/src/stories/DataBinding.advanced.stories.tsx +432 -432
- package/src/stories/DataProvider.stories.tsx +1192 -1192
- package/src/stories/FeatureCard.stories.tsx +557 -557
- package/src/stories/FeatureGrid.stories.tsx +594 -594
- package/src/stories/Footer.stories.tsx +640 -640
- package/src/stories/FormBlock.stories.tsx +760 -760
- package/src/stories/FormComponents.stories.tsx +349 -541
- package/src/stories/GridCell.stories.tsx +417 -0
- package/src/stories/GridLayout.stories.tsx +353 -0
- package/src/stories/HeroBlock.stories.tsx +862 -373
- package/src/stories/HtmlInputField.stories.tsx +474 -474
- package/src/stories/Image.stories.tsx +819 -0
- package/src/stories/Introduction.stories.tsx +667 -667
- package/src/stories/LayoutBlocks.stories.tsx +324 -324
- package/src/stories/Logo.stories.tsx +165 -6
- package/src/stories/Markdown.stories.tsx +137 -137
- package/src/stories/ModelView.stories.tsx +477 -0
- package/src/stories/Page.stories.tsx +688 -688
- package/src/stories/PageBannerHeader.stories.tsx +864 -864
- package/src/stories/PaletteSwitcher.stories.tsx +119 -119
- package/src/stories/ProductCard.stories.tsx +424 -424
- package/src/stories/QwickApp.stories.tsx +368 -368
- package/src/stories/ResponsiveMenu.stories.tsx +249 -249
- package/src/stories/SafeSpan.stories.tsx +531 -531
- package/src/stories/Section.stories.tsx +90 -2
- package/src/stories/SelectInputField.stories.tsx +524 -524
- package/src/stories/Text.stories.tsx +560 -0
- package/src/stories/TextInputField.stories.tsx +443 -443
- package/src/stories/ThemeSwitcher.stories.tsx +123 -123
- package/src/utils/htmlTransform.tsx +74 -53
- package/src/utils/reactUtils.tsx +57 -6
- package/dist/index.bundled.css +0 -12
- /package/src/{hooks/__tests__ → __tests__/hooks}/useDataBinding.test.tsx.disabled +0 -0
- /package/src/{schemas/__tests__ → __tests__/schemas}/builders.test.ts +0 -0
- /package/src/{utils/__tests__ → __tests__/utils}/createDataDrivenComponent.test.tsx.disabled +0 -0
- /package/src/{utils/__tests__ → __tests__/utils}/htmlTransform.test.tsx +0 -0
- /package/src/{utils/__tests__ → __tests__/utils}/optional-logging.test.ts +0 -0
|
@@ -424,10 +424,15 @@ function CoverImageHeader(props: CoverImageHeaderProps) {
|
|
|
424
424
|
|
|
425
425
|
// Convert HeaderActionModel[] to HeaderAction[] if actions exist
|
|
426
426
|
const { actions: modelActions, ...viewProps } = coverImageHeaderProps;
|
|
427
|
-
const convertedActions: HeaderAction[] = modelActions
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
427
|
+
const convertedActions: HeaderAction[] = modelActions
|
|
428
|
+
? modelActions
|
|
429
|
+
.filter(action => typeof action.id === 'string' && !!action.id)
|
|
430
|
+
.map(action => ({
|
|
431
|
+
...action,
|
|
432
|
+
id: action.id as string,
|
|
433
|
+
onClick: () => console.log(`Action clicked: ${action.id}`) // Default handler for data-driven actions
|
|
434
|
+
}))
|
|
435
|
+
: [];
|
|
431
436
|
|
|
432
437
|
return <CoverImageHeaderView {...viewProps} actions={convertedActions} />;
|
|
433
438
|
}
|
|
@@ -237,10 +237,9 @@ function FeatureCardView({
|
|
|
237
237
|
<Button
|
|
238
238
|
key={action.id}
|
|
239
239
|
variant={action.variant || 'contained'}
|
|
240
|
-
color={action.color || 'primary'}
|
|
240
|
+
// color={action.color || 'primary'}
|
|
241
241
|
disabled={action.disabled}
|
|
242
242
|
onClick={action.onClick}
|
|
243
|
-
size="small"
|
|
244
243
|
>
|
|
245
244
|
{action.label}
|
|
246
245
|
</Button>
|
|
@@ -140,7 +140,25 @@ function FeatureGrid(props: FeatureGridProps) {
|
|
|
140
140
|
return null;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
// Ensure features have a non-optional 'id'
|
|
144
|
+
const safeFeatures =
|
|
145
|
+
Array.isArray(featureGridProps.features)
|
|
146
|
+
? featureGridProps.features.map((f, idx) => ({
|
|
147
|
+
...f,
|
|
148
|
+
id: f.id ?? `feature-${idx}`,
|
|
149
|
+
title: f.title ?? '',
|
|
150
|
+
description: f.description ?? '',
|
|
151
|
+
}))
|
|
152
|
+
: featureGridProps.features;
|
|
153
|
+
|
|
154
|
+
// Ensure columns is one of the allowed values
|
|
155
|
+
const allowedColumns = [1, 2, 3, 4, 5, 6];
|
|
156
|
+
const columns =
|
|
157
|
+
allowedColumns.includes(featureGridProps.columns as number)
|
|
158
|
+
? (featureGridProps.columns as 1 | 2 | 3 | 4 | 5 | 6)
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
return <FeatureGridView {...featureGridProps} features={safeFeatures} columns={columns} />;
|
|
144
162
|
}
|
|
145
163
|
|
|
146
164
|
export default FeatureGrid;
|
|
@@ -337,7 +337,19 @@ function Footer(props: FooterProps) {
|
|
|
337
337
|
return null;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
-
|
|
340
|
+
// Map FooterSectionModel[] to FooterSection[] and ensure id is a string
|
|
341
|
+
const mappedSections =
|
|
342
|
+
footerProps.sections?.map(section => ({
|
|
343
|
+
...section,
|
|
344
|
+
id: section.id ?? '', // fallback to empty string if id is undefined
|
|
345
|
+
items: section.items?.map(item => ({
|
|
346
|
+
...item,
|
|
347
|
+
id: item.id ?? '', // fallback to empty string if id is undefined
|
|
348
|
+
label: item.label ?? '', // ensure label is always a string
|
|
349
|
+
})) ?? [],
|
|
350
|
+
})) ?? [];
|
|
351
|
+
|
|
352
|
+
return <FooterView {...footerProps} sections={mappedSections} />;
|
|
341
353
|
}
|
|
342
354
|
|
|
343
355
|
export default Footer;
|
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HeroBlock Component - Full-width hero section with
|
|
3
|
-
*
|
|
4
|
-
* Enhanced with data binding support through dataSource prop.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* - Traditional: <HeroBlock title="Welcome" subtitle="Get started" actions={[...]} />
|
|
8
|
-
* - Data-driven: <HeroBlock dataSource="pages.home.hero" />
|
|
2
|
+
* HeroBlock Component - Full-width hero section with serialization support
|
|
9
3
|
*
|
|
10
4
|
* Features:
|
|
11
5
|
* - Responsive headline, subtitle, and actions
|
|
12
6
|
* - Supports background images, gradients, and theme colors
|
|
13
7
|
* - Overlay for image backgrounds
|
|
14
8
|
* - Customizable height, alignment, and overlay opacity
|
|
9
|
+
* - Full serialization support via ModelView
|
|
10
|
+
* - Nested Button component serialization support
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* - Traditional: <HeroBlock title="Welcome" subtitle="Get started" actions={[...]} />
|
|
14
|
+
* - Data-driven: <HeroBlock dataSource="pages.home.hero" />
|
|
15
|
+
* - Serializable: ComponentTransformer.serialize(<HeroBlock ... />)
|
|
15
16
|
*
|
|
16
17
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
20
|
import { Box, Container, Stack, Typography, useTheme } from '@mui/material';
|
|
20
|
-
import {
|
|
21
|
-
import React from 'react';
|
|
22
|
-
import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding
|
|
21
|
+
import type { ModelProps, WithDataBinding } from '@qwickapps/schema';
|
|
22
|
+
import React, { ReactElement, ReactNode } from 'react';
|
|
23
|
+
import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../../hooks';
|
|
23
24
|
import HeroBlockModel from '../../schemas/HeroBlockSchema';
|
|
25
|
+
import { ModelView } from '../base/ModelView';
|
|
24
26
|
import { Button, ButtonProps } from '../buttons/Button';
|
|
25
27
|
|
|
26
|
-
type HeroBlockViewProps =
|
|
28
|
+
type HeroBlockViewProps = ModelProps<HeroBlockModel> & {
|
|
27
29
|
/** Action buttons (data-driven) */
|
|
28
30
|
actions?: ButtonProps[];
|
|
29
31
|
/** Additional content below the text */
|
|
30
|
-
children?:
|
|
32
|
+
children?: ReactNode;
|
|
33
|
+
/** Click handler for the hero section */
|
|
34
|
+
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
35
|
+
/** Additional inline styles */
|
|
36
|
+
style?: React.CSSProperties;
|
|
31
37
|
};
|
|
32
38
|
|
|
33
39
|
export interface HeroBlockProps extends HeroBlockViewProps, WithDataBinding {}
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
* Core HeroBlock View component - handles hero section rendering
|
|
37
|
-
*/
|
|
41
|
+
// View component - handles the actual rendering
|
|
38
42
|
function HeroBlockView({
|
|
39
43
|
title = '',
|
|
40
44
|
subtitle,
|
|
@@ -230,17 +234,77 @@ function HeroBlockView({
|
|
|
230
234
|
);
|
|
231
235
|
}
|
|
232
236
|
|
|
233
|
-
|
|
234
|
-
|
|
237
|
+
// Main component with data binding support and serialization capability
|
|
238
|
+
export class HeroBlock extends ModelView<HeroBlockProps, HeroBlockModel> {
|
|
239
|
+
// Component self-declaration for serialization
|
|
240
|
+
static readonly tagName = 'HeroBlock';
|
|
241
|
+
static readonly version = '1.0.0';
|
|
242
|
+
|
|
243
|
+
// Deserialization: JSON data → React element
|
|
244
|
+
static fromJson(jsonData: any): ReactElement {
|
|
245
|
+
return <HeroBlock {...jsonData} />;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Component-specific serialization properties
|
|
249
|
+
protected getComponentSpecificProps(): any {
|
|
250
|
+
return {
|
|
251
|
+
title: this.props.title,
|
|
252
|
+
subtitle: this.props.subtitle,
|
|
253
|
+
backgroundImage: this.props.backgroundImage,
|
|
254
|
+
backgroundGradient: this.props.backgroundGradient,
|
|
255
|
+
backgroundColor: this.props.backgroundColor,
|
|
256
|
+
textAlign: this.props.textAlign,
|
|
257
|
+
blockHeight: this.props.blockHeight,
|
|
258
|
+
overlayOpacity: this.props.overlayOpacity,
|
|
259
|
+
// Serialize actions array (ButtonProps)
|
|
260
|
+
actions: this.props.actions
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Override serializeChildren to handle nested Button components
|
|
265
|
+
protected serializeChildren(children: ReactNode): any {
|
|
266
|
+
if (typeof children === 'string') {
|
|
267
|
+
return children;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// HeroBlock can contain complex nested content, but not necessarily other serializable components
|
|
271
|
+
// Handle the children as-is for now, since HeroBlock typically contains MUI Typography and Box elements
|
|
272
|
+
// The actions prop is handled separately in getComponentSpecificProps
|
|
273
|
+
if (children) {
|
|
274
|
+
// For HeroBlock, children are typically additional content (Typography, Box)
|
|
275
|
+
// These don't need component serialization, just text extraction
|
|
276
|
+
const { extractTextFromReactNode } = require('../../utils/reactUtils');
|
|
277
|
+
return extractTextFromReactNode(children);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
235
282
|
|
|
236
|
-
//
|
|
237
|
-
|
|
283
|
+
// Indicate that HeroBlock doesn't contain nested serializable components in children
|
|
284
|
+
// (actions are handled separately via props)
|
|
285
|
+
protected hasNestedComponents(children: ReactNode): boolean {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// HeroBlock component renders traditional props view
|
|
290
|
+
protected renderView(): React.ReactElement {
|
|
291
|
+
const { dataSource, bindingOptions, ...restProps } = this.props;
|
|
238
292
|
return <HeroBlockView {...restProps} />;
|
|
239
293
|
}
|
|
240
294
|
|
|
295
|
+
// HeroBlock component renders data-bound view
|
|
296
|
+
protected renderWithDataBinding(): React.ReactElement {
|
|
297
|
+
return <HeroBlockWithDataBinding {...this.props} />;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Helper component to handle data binding with hooks (since we can't use hooks in class components)
|
|
302
|
+
function HeroBlockWithDataBinding(props: HeroBlockProps) {
|
|
303
|
+
const { dataSource, bindingOptions, ...restProps } = props;
|
|
304
|
+
|
|
241
305
|
// Use data binding
|
|
242
306
|
const { dataSource: _source, loading, error, cached, ...heroProps } = useDataBinding<HeroBlockModel>(
|
|
243
|
-
dataSource
|
|
307
|
+
dataSource!,
|
|
244
308
|
restProps as Partial<HeroBlockModel>,
|
|
245
309
|
HeroBlockModel.getSchema(),
|
|
246
310
|
{ cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
|
|
@@ -277,4 +341,7 @@ function HeroBlock(props: HeroBlockProps) {
|
|
|
277
341
|
return <HeroBlockView {...heroProps} />;
|
|
278
342
|
}
|
|
279
343
|
|
|
344
|
+
// Mark as QwickApp component
|
|
345
|
+
(HeroBlock as any)[QWICKAPP_COMPONENT] = true;
|
|
346
|
+
|
|
280
347
|
export default HeroBlock;
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image - Comprehensive image display component with serialization support
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Responsive image handling with srcSet and sizes
|
|
6
|
+
* - Multiple fit modes and positioning options
|
|
7
|
+
* - Loading states and error handling
|
|
8
|
+
* - Accessibility support with proper alt text
|
|
9
|
+
* - Lazy loading support
|
|
10
|
+
* - Fallback image handling
|
|
11
|
+
* - Full serialization support via ModelView
|
|
12
|
+
*
|
|
13
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { ReactElement, useState, useCallback } from 'react';
|
|
17
|
+
import { Box, Skeleton, Typography, useTheme } from '@mui/material';
|
|
18
|
+
import { BrokenImage as BrokenImageIcon } from '@mui/icons-material';
|
|
19
|
+
import type { WithDataBinding, ModelProps } from '@qwickapps/schema';
|
|
20
|
+
import { QWICKAPP_COMPONENT, useBaseProps, useDataBinding } from '../../hooks';
|
|
21
|
+
import ImageModel, { ImageFit, ImageLoading, ImagePosition } from '../../schemas/ImageSchema';
|
|
22
|
+
import { ModelView } from '../base/ModelView';
|
|
23
|
+
|
|
24
|
+
type ImageViewProps = ModelProps<ImageModel> & {
|
|
25
|
+
/** Click handler for the image */
|
|
26
|
+
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
27
|
+
/** Additional inline styles */
|
|
28
|
+
style?: React.CSSProperties;
|
|
29
|
+
/** Additional CSS class names */
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export interface ImageProps extends ImageViewProps, WithDataBinding {}
|
|
34
|
+
|
|
35
|
+
// View component - handles the actual rendering
|
|
36
|
+
function ImageView({
|
|
37
|
+
src,
|
|
38
|
+
alt = '',
|
|
39
|
+
width,
|
|
40
|
+
height,
|
|
41
|
+
objectFit = 'cover',
|
|
42
|
+
objectPosition = 'center',
|
|
43
|
+
loading = 'lazy',
|
|
44
|
+
title,
|
|
45
|
+
draggable = false,
|
|
46
|
+
borderRadius,
|
|
47
|
+
showLoading = false,
|
|
48
|
+
showError = false,
|
|
49
|
+
fallbackSrc,
|
|
50
|
+
sizes,
|
|
51
|
+
srcSet,
|
|
52
|
+
loadingPlaceholder,
|
|
53
|
+
errorPlaceholder,
|
|
54
|
+
onClick,
|
|
55
|
+
style,
|
|
56
|
+
className,
|
|
57
|
+
...restProps
|
|
58
|
+
}: ImageViewProps) {
|
|
59
|
+
const { styleProps, htmlProps, restProps: otherProps } = useBaseProps(restProps);
|
|
60
|
+
const theme = useTheme();
|
|
61
|
+
|
|
62
|
+
// Mark as QwickApp component
|
|
63
|
+
(ImageView as any)[QWICKAPP_COMPONENT] = true;
|
|
64
|
+
|
|
65
|
+
const [imageState, setImageState] = useState<'loading' | 'loaded' | 'error'>('loading');
|
|
66
|
+
const [currentSrc, setCurrentSrc] = useState(src);
|
|
67
|
+
|
|
68
|
+
const handleLoad = useCallback(() => {
|
|
69
|
+
setImageState('loaded');
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const handleError = useCallback(() => {
|
|
73
|
+
// Try fallback source if available and not already using it
|
|
74
|
+
if (fallbackSrc && currentSrc !== fallbackSrc) {
|
|
75
|
+
setCurrentSrc(fallbackSrc);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
setImageState('error');
|
|
79
|
+
}, [fallbackSrc, currentSrc]);
|
|
80
|
+
|
|
81
|
+
// Early return if no src provided
|
|
82
|
+
if (!src) {
|
|
83
|
+
if (showError) {
|
|
84
|
+
return (
|
|
85
|
+
<Box
|
|
86
|
+
{...htmlProps}
|
|
87
|
+
{...styleProps}
|
|
88
|
+
{...otherProps}
|
|
89
|
+
className={`image-error ${className || ''}`.trim()}
|
|
90
|
+
style={{
|
|
91
|
+
display: 'flex',
|
|
92
|
+
alignItems: 'center',
|
|
93
|
+
justifyContent: 'center',
|
|
94
|
+
backgroundColor: theme.palette.grey[100],
|
|
95
|
+
color: theme.palette.text.secondary,
|
|
96
|
+
width: width || 200,
|
|
97
|
+
height: height || 150,
|
|
98
|
+
borderRadius,
|
|
99
|
+
...style
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{errorPlaceholder || (
|
|
103
|
+
<Box sx={{ textAlign: 'center' }}>
|
|
104
|
+
<BrokenImageIcon sx={{ fontSize: 48, mb: 1, opacity: 0.5 }} />
|
|
105
|
+
<Typography variant="body2" color="text.secondary">
|
|
106
|
+
No image source
|
|
107
|
+
</Typography>
|
|
108
|
+
</Box>
|
|
109
|
+
)}
|
|
110
|
+
</Box>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Loading state
|
|
117
|
+
if (imageState === 'loading' && showLoading) {
|
|
118
|
+
return (
|
|
119
|
+
<Box
|
|
120
|
+
{...htmlProps}
|
|
121
|
+
{...styleProps}
|
|
122
|
+
{...otherProps}
|
|
123
|
+
className={`image-loading ${className || ''}`.trim()}
|
|
124
|
+
style={{
|
|
125
|
+
width: width || '100%',
|
|
126
|
+
height: height || 200,
|
|
127
|
+
borderRadius,
|
|
128
|
+
...style
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
{loadingPlaceholder || (
|
|
132
|
+
<Skeleton
|
|
133
|
+
variant="rectangular"
|
|
134
|
+
width="100%"
|
|
135
|
+
height="100%"
|
|
136
|
+
sx={{ borderRadius }}
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
139
|
+
</Box>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Error state
|
|
144
|
+
if (imageState === 'error' && showError) {
|
|
145
|
+
return (
|
|
146
|
+
<Box
|
|
147
|
+
{...htmlProps}
|
|
148
|
+
{...styleProps}
|
|
149
|
+
{...otherProps}
|
|
150
|
+
className={`image-error ${className || ''}`.trim()}
|
|
151
|
+
style={{
|
|
152
|
+
display: 'flex',
|
|
153
|
+
alignItems: 'center',
|
|
154
|
+
justifyContent: 'center',
|
|
155
|
+
backgroundColor: theme.palette.grey[100],
|
|
156
|
+
color: theme.palette.text.secondary,
|
|
157
|
+
width: width || '100%',
|
|
158
|
+
height: height || 200,
|
|
159
|
+
borderRadius,
|
|
160
|
+
...style
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
{errorPlaceholder || (
|
|
164
|
+
<Box sx={{ textAlign: 'center', p: 2 }}>
|
|
165
|
+
<BrokenImageIcon sx={{ fontSize: 48, mb: 1, opacity: 0.5 }} />
|
|
166
|
+
<Typography variant="body2" color="text.secondary">
|
|
167
|
+
Failed to load image
|
|
168
|
+
</Typography>
|
|
169
|
+
{fallbackSrc && (
|
|
170
|
+
<Typography variant="caption" color="text.secondary">
|
|
171
|
+
Fallback image also failed
|
|
172
|
+
</Typography>
|
|
173
|
+
)}
|
|
174
|
+
</Box>
|
|
175
|
+
)}
|
|
176
|
+
</Box>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const imageStyles: React.CSSProperties = {
|
|
181
|
+
display: 'block',
|
|
182
|
+
maxWidth: '100%',
|
|
183
|
+
height: 'auto',
|
|
184
|
+
objectFit,
|
|
185
|
+
objectPosition,
|
|
186
|
+
borderRadius,
|
|
187
|
+
cursor: onClick ? 'pointer' : 'default',
|
|
188
|
+
...style
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Apply dimensions if provided
|
|
192
|
+
if (width) imageStyles.width = width;
|
|
193
|
+
if (height) imageStyles.height = height;
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<img
|
|
197
|
+
{...htmlProps}
|
|
198
|
+
{...styleProps}
|
|
199
|
+
{...otherProps}
|
|
200
|
+
src={currentSrc}
|
|
201
|
+
alt={alt}
|
|
202
|
+
width={width}
|
|
203
|
+
height={height}
|
|
204
|
+
loading={loading}
|
|
205
|
+
title={title}
|
|
206
|
+
draggable={draggable}
|
|
207
|
+
sizes={sizes}
|
|
208
|
+
srcSet={srcSet}
|
|
209
|
+
className={`image ${className || ''}`.trim()}
|
|
210
|
+
style={imageStyles}
|
|
211
|
+
onClick={onClick}
|
|
212
|
+
onLoad={handleLoad}
|
|
213
|
+
onError={handleError}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Main component with data binding support and serialization capability
|
|
219
|
+
export class Image extends ModelView<ImageProps, ImageModel> {
|
|
220
|
+
// Component self-declaration for serialization
|
|
221
|
+
static readonly tagName = 'Image';
|
|
222
|
+
static readonly version = '1.0.0';
|
|
223
|
+
|
|
224
|
+
// Deserialization: JSON data → React element
|
|
225
|
+
static fromJson(jsonData: any): ReactElement {
|
|
226
|
+
return <Image {...jsonData} />;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Component-specific serialization properties
|
|
230
|
+
protected getComponentSpecificProps(): any {
|
|
231
|
+
return {
|
|
232
|
+
src: this.props.src,
|
|
233
|
+
alt: this.props.alt,
|
|
234
|
+
width: this.props.width,
|
|
235
|
+
height: this.props.height,
|
|
236
|
+
objectFit: this.props.objectFit,
|
|
237
|
+
objectPosition: this.props.objectPosition,
|
|
238
|
+
loading: this.props.loading,
|
|
239
|
+
title: this.props.title,
|
|
240
|
+
draggable: this.props.draggable,
|
|
241
|
+
borderRadius: this.props.borderRadius,
|
|
242
|
+
showLoading: this.props.showLoading,
|
|
243
|
+
showError: this.props.showError,
|
|
244
|
+
fallbackSrc: this.props.fallbackSrc,
|
|
245
|
+
sizes: this.props.sizes,
|
|
246
|
+
srcSet: this.props.srcSet,
|
|
247
|
+
// Note: loadingPlaceholder and errorPlaceholder are ReactNodes
|
|
248
|
+
// and will be handled by base serialization if needed
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Image component renders traditional props view
|
|
253
|
+
protected renderView(): React.ReactElement {
|
|
254
|
+
const { dataSource, bindingOptions, ...restProps } = this.props;
|
|
255
|
+
return <ImageView {...restProps} />;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Image component renders data-bound view
|
|
259
|
+
protected renderWithDataBinding(): React.ReactElement {
|
|
260
|
+
return <ImageWithDataBinding {...this.props} />;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Register HTML patterns that Image component can handle
|
|
264
|
+
static registerPatternHandlers(registry: any): void {
|
|
265
|
+
// Register img elements
|
|
266
|
+
if (!registry.hasPattern('img')) {
|
|
267
|
+
registry.registerPattern('img', Image.transformImage);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Register figure elements with img
|
|
271
|
+
if (!registry.hasPattern('figure img')) {
|
|
272
|
+
registry.registerPattern('figure img', Image.transformFigureImage);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Transform img elements to Image component
|
|
277
|
+
private static transformImage(element: Element): any {
|
|
278
|
+
const src = element.getAttribute('src') || '';
|
|
279
|
+
const alt = element.getAttribute('alt') || '';
|
|
280
|
+
const width = element.getAttribute('width');
|
|
281
|
+
const height = element.getAttribute('height');
|
|
282
|
+
const loading = element.getAttribute('loading') as 'lazy' | 'eager' | undefined;
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
tagName: 'Image',
|
|
286
|
+
props: {
|
|
287
|
+
src,
|
|
288
|
+
alt,
|
|
289
|
+
width: width ? parseInt(width) : undefined,
|
|
290
|
+
height: height ? parseInt(height) : undefined,
|
|
291
|
+
loading: loading || 'lazy'
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Transform figure > img elements to Image component with caption support
|
|
297
|
+
private static transformFigureImage(element: Element): any {
|
|
298
|
+
const figure = element.closest('figure');
|
|
299
|
+
const figcaption = figure?.querySelector('figcaption');
|
|
300
|
+
const caption = figcaption?.textContent || '';
|
|
301
|
+
|
|
302
|
+
const src = element.getAttribute('src') || '';
|
|
303
|
+
const alt = element.getAttribute('alt') || caption;
|
|
304
|
+
const width = element.getAttribute('width');
|
|
305
|
+
const height = element.getAttribute('height');
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
tagName: 'Image',
|
|
309
|
+
props: {
|
|
310
|
+
src,
|
|
311
|
+
alt,
|
|
312
|
+
caption: caption || undefined,
|
|
313
|
+
width: width ? parseInt(width) : undefined,
|
|
314
|
+
height: height ? parseInt(height) : undefined,
|
|
315
|
+
loading: 'lazy'
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Helper component to handle data binding with hooks (since we can't use hooks in class components)
|
|
322
|
+
function ImageWithDataBinding(props: ImageProps) {
|
|
323
|
+
const { dataSource, bindingOptions, ...restProps } = props;
|
|
324
|
+
|
|
325
|
+
// Use data binding
|
|
326
|
+
const { dataSource: _source, loading, error, cached, ...rawImageProps } = useDataBinding<ImageModel>(
|
|
327
|
+
dataSource!,
|
|
328
|
+
restProps as Partial<ImageModel>,
|
|
329
|
+
ImageModel.getSchema(),
|
|
330
|
+
{ cache: true, cacheTTL: 300000, strict: false, ...bindingOptions }
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Use props directly since new serialization system handles component-level transformation
|
|
334
|
+
const imageProps = rawImageProps;
|
|
335
|
+
|
|
336
|
+
// Show loading state
|
|
337
|
+
if (loading) {
|
|
338
|
+
return (
|
|
339
|
+
<Box
|
|
340
|
+
sx={{
|
|
341
|
+
display: 'flex',
|
|
342
|
+
alignItems: 'center',
|
|
343
|
+
justifyContent: 'center',
|
|
344
|
+
backgroundColor: 'grey.100',
|
|
345
|
+
width: imageProps.width || 200,
|
|
346
|
+
height: imageProps.height || 150,
|
|
347
|
+
borderRadius: imageProps.borderRadius
|
|
348
|
+
}}
|
|
349
|
+
>
|
|
350
|
+
<Skeleton
|
|
351
|
+
variant="rectangular"
|
|
352
|
+
width="100%"
|
|
353
|
+
height="100%"
|
|
354
|
+
sx={{ borderRadius: imageProps.borderRadius }}
|
|
355
|
+
/>
|
|
356
|
+
</Box>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (error) {
|
|
361
|
+
console.error('Error loading image:', error);
|
|
362
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
363
|
+
return (
|
|
364
|
+
<Box
|
|
365
|
+
sx={{
|
|
366
|
+
display: 'flex',
|
|
367
|
+
flexDirection: 'column',
|
|
368
|
+
alignItems: 'center',
|
|
369
|
+
justifyContent: 'center',
|
|
370
|
+
backgroundColor: 'error.light',
|
|
371
|
+
color: 'error.contrastText',
|
|
372
|
+
p: 2,
|
|
373
|
+
borderRadius: imageProps.borderRadius,
|
|
374
|
+
width: imageProps.width || 200,
|
|
375
|
+
height: imageProps.height || 150
|
|
376
|
+
}}
|
|
377
|
+
>
|
|
378
|
+
<BrokenImageIcon sx={{ fontSize: 48, mb: 1 }} />
|
|
379
|
+
<Typography variant="body2" align="center">
|
|
380
|
+
Error Loading Image
|
|
381
|
+
</Typography>
|
|
382
|
+
<Typography variant="caption" align="center">
|
|
383
|
+
{error.message}
|
|
384
|
+
</Typography>
|
|
385
|
+
</Box>
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
console.log('Resolved props for Image:', imageProps);
|
|
392
|
+
return <ImageView {...imageProps} />;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export default Image;
|
|
@@ -397,18 +397,20 @@ function PageBannerHeader(props: PageBannerHeaderProps) {
|
|
|
397
397
|
);
|
|
398
398
|
|
|
399
399
|
// Convert HeaderActionModel[] to HeaderAction[] by adding default onClick handlers
|
|
400
|
-
const convertedActions: HeaderAction[] | undefined = modelProps.actions
|
|
401
|
-
id: action.id
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
400
|
+
const convertedActions: HeaderAction[] | undefined = modelProps.actions
|
|
401
|
+
?.filter((action): action is typeof action & { id: string } => typeof action.id === 'string')
|
|
402
|
+
.map(action => ({
|
|
403
|
+
id: action.id,
|
|
404
|
+
label: action.label,
|
|
405
|
+
icon: action.icon as React.ReactNode,
|
|
406
|
+
disabled: action.disabled,
|
|
407
|
+
destructive: action.destructive,
|
|
408
|
+
priority: action.priority,
|
|
409
|
+
onClick: () => {
|
|
410
|
+
console.log(`Action clicked: ${action.id} - ${action.label}`);
|
|
411
|
+
// In a real app, this would dispatch events or call configured handlers
|
|
412
|
+
}
|
|
413
|
+
}));
|
|
412
414
|
|
|
413
415
|
const pageBannerHeaderProps: PageBannerHeaderViewProps = {
|
|
414
416
|
...modelProps,
|
|
@@ -380,7 +380,7 @@ function ProductCardView({
|
|
|
380
380
|
<Button
|
|
381
381
|
key={action.id}
|
|
382
382
|
variant={action.variant || 'contained'}
|
|
383
|
-
color={action.color || 'primary'}
|
|
383
|
+
// color={action.color || 'primary'}
|
|
384
384
|
disabled={action.disabled}
|
|
385
385
|
onClick={action.onClick}
|
|
386
386
|
{...(variant === 'compact' && { fullWidth: true })}
|