@qwickapps/react-framework 1.3.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/LICENSE +44 -0
- package/README.md +794 -0
- package/dist/components/AccessibilityChecker.d.ts +12 -0
- package/dist/components/AccessibilityChecker.d.ts.map +1 -0
- package/dist/components/Html.d.ts +48 -0
- package/dist/components/Html.d.ts.map +1 -0
- package/dist/components/Logo.d.ts +79 -0
- package/dist/components/Logo.d.ts.map +1 -0
- package/dist/components/Markdown.d.ts +47 -0
- package/dist/components/Markdown.d.ts.map +1 -0
- package/dist/components/QwickApp.d.ts +56 -0
- package/dist/components/QwickApp.d.ts.map +1 -0
- package/dist/components/QwickAppsLogo.d.ts +25 -0
- package/dist/components/QwickAppsLogo.d.ts.map +1 -0
- package/dist/components/ResponsiveMenu.d.ts +38 -0
- package/dist/components/ResponsiveMenu.d.ts.map +1 -0
- package/dist/components/SafeSpan.d.ts +23 -0
- package/dist/components/SafeSpan.d.ts.map +1 -0
- package/dist/components/Scaffold.d.ts +57 -0
- package/dist/components/Scaffold.d.ts.map +1 -0
- package/dist/components/blocks/Article.d.ts +23 -0
- package/dist/components/blocks/Article.d.ts.map +1 -0
- package/dist/components/blocks/CardListGrid.d.ts +23 -0
- package/dist/components/blocks/CardListGrid.d.ts.map +1 -0
- package/dist/components/blocks/Code.d.ts +21 -0
- package/dist/components/blocks/Code.d.ts.map +1 -0
- package/dist/components/blocks/Content.d.ts +24 -0
- package/dist/components/blocks/Content.d.ts.map +1 -0
- package/dist/components/blocks/CoverImageHeader.d.ts +44 -0
- package/dist/components/blocks/CoverImageHeader.d.ts.map +1 -0
- package/dist/components/blocks/FeatureCard.d.ts +66 -0
- package/dist/components/blocks/FeatureCard.d.ts.map +1 -0
- package/dist/components/blocks/FeatureGrid.d.ts +48 -0
- package/dist/components/blocks/FeatureGrid.d.ts.map +1 -0
- package/dist/components/blocks/Footer.d.ts +56 -0
- package/dist/components/blocks/Footer.d.ts.map +1 -0
- package/dist/components/blocks/HeroBlock.d.ts +33 -0
- package/dist/components/blocks/HeroBlock.d.ts.map +1 -0
- package/dist/components/blocks/PageBannerHeader.d.ts +30 -0
- package/dist/components/blocks/PageBannerHeader.d.ts.map +1 -0
- package/dist/components/blocks/ProductCard.d.ts +57 -0
- package/dist/components/blocks/ProductCard.d.ts.map +1 -0
- package/dist/components/blocks/Section.d.ts +40 -0
- package/dist/components/blocks/Section.d.ts.map +1 -0
- package/dist/components/blocks/index.d.ts +37 -0
- package/dist/components/blocks/index.d.ts.map +1 -0
- package/dist/components/buttons/Button.d.ts +38 -0
- package/dist/components/buttons/Button.d.ts.map +1 -0
- package/dist/components/buttons/PaletteSwitcher.d.ts +24 -0
- package/dist/components/buttons/PaletteSwitcher.d.ts.map +1 -0
- package/dist/components/buttons/ThemeSwitcher.d.ts +24 -0
- package/dist/components/buttons/ThemeSwitcher.d.ts.map +1 -0
- package/dist/components/buttons/index.d.ts +11 -0
- package/dist/components/buttons/index.d.ts.map +1 -0
- package/dist/components/forms/FormBlock.d.ts +45 -0
- package/dist/components/forms/FormBlock.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +8 -0
- package/dist/components/forms/index.d.ts.map +1 -0
- package/dist/components/index.d.ts +32 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/input/ChoiceInputField.d.ts +30 -0
- package/dist/components/input/ChoiceInputField.d.ts.map +1 -0
- package/dist/components/input/HtmlInputField.d.ts +29 -0
- package/dist/components/input/HtmlInputField.d.ts.map +1 -0
- package/dist/components/input/SelectInputField.d.ts +29 -0
- package/dist/components/input/SelectInputField.d.ts.map +1 -0
- package/dist/components/input/TextField.d.ts +18 -0
- package/dist/components/input/TextField.d.ts.map +1 -0
- package/dist/components/input/TextInputField.d.ts +32 -0
- package/dist/components/input/TextInputField.d.ts.map +1 -0
- package/dist/components/input/index.d.ts +17 -0
- package/dist/components/input/index.d.ts.map +1 -0
- package/dist/components/layout/GridCell.d.ts +16 -0
- package/dist/components/layout/GridCell.d.ts.map +1 -0
- package/dist/components/layout/GridCellWrapper.d.ts +46 -0
- package/dist/components/layout/GridCellWrapper.d.ts.map +1 -0
- package/dist/components/layout/GridLayout.d.ts +38 -0
- package/dist/components/layout/GridLayout.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +12 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/menu/Menu.d.ts +1 -0
- package/dist/components/menu/Menu.d.ts.map +1 -0
- package/dist/components/menu/MenuItem.d.ts +31 -0
- package/dist/components/menu/MenuItem.d.ts.map +1 -0
- package/dist/components/menu/index.d.ts +7 -0
- package/dist/components/menu/index.d.ts.map +1 -0
- package/dist/components/pages/FormPage.d.ts +66 -0
- package/dist/components/pages/FormPage.d.ts.map +1 -0
- package/dist/components/pages/Page.d.ts +124 -0
- package/dist/components/pages/Page.d.ts.map +1 -0
- package/dist/components/pages/index.d.ts +11 -0
- package/dist/components/pages/index.d.ts.map +1 -0
- package/dist/contexts/DataContext.d.ts +139 -0
- package/dist/contexts/DataContext.d.ts.map +1 -0
- package/dist/contexts/DimensionsContext.d.ts +42 -0
- package/dist/contexts/DimensionsContext.d.ts.map +1 -0
- package/dist/contexts/PaletteContext.d.ts +53 -0
- package/dist/contexts/PaletteContext.d.ts.map +1 -0
- package/dist/contexts/QwickAppContext.d.ts +71 -0
- package/dist/contexts/QwickAppContext.d.ts.map +1 -0
- package/dist/contexts/ThemeContext.d.ts +65 -0
- package/dist/contexts/ThemeContext.d.ts.map +1 -0
- package/dist/contexts/index.d.ts +9 -0
- package/dist/contexts/index.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/useBaseProps.d.ts +101 -0
- package/dist/hooks/useBaseProps.d.ts.map +1 -0
- package/dist/hooks/useDataBinding.d.ts +22 -0
- package/dist/hooks/useDataBinding.d.ts.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.css +1 -0
- package/dist/index.esm.js +24143 -0
- package/dist/index.js +24245 -0
- package/dist/palettes/PaletteAutumn.d.ts +10 -0
- package/dist/palettes/PaletteAutumn.d.ts.map +1 -0
- package/dist/palettes/PaletteCosmic.d.ts +10 -0
- package/dist/palettes/PaletteCosmic.d.ts.map +1 -0
- package/dist/palettes/PaletteDefault.d.ts +10 -0
- package/dist/palettes/PaletteDefault.d.ts.map +1 -0
- package/dist/palettes/PaletteOcean.d.ts +10 -0
- package/dist/palettes/PaletteOcean.d.ts.map +1 -0
- package/dist/palettes/PaletteSpring.d.ts +10 -0
- package/dist/palettes/PaletteSpring.d.ts.map +1 -0
- package/dist/palettes/PaletteWinter.d.ts +10 -0
- package/dist/palettes/PaletteWinter.d.ts.map +1 -0
- package/dist/palettes/index.d.ts +13 -0
- package/dist/palettes/index.d.ts.map +1 -0
- package/dist/schemas/ActionSchema.d.ts +21 -0
- package/dist/schemas/ActionSchema.d.ts.map +1 -0
- package/dist/schemas/ArticleSchema.d.ts +13 -0
- package/dist/schemas/ArticleSchema.d.ts.map +1 -0
- package/dist/schemas/Builders.d.ts +7 -0
- package/dist/schemas/Builders.d.ts.map +1 -0
- package/dist/schemas/ButtonSchema.d.ts +19 -0
- package/dist/schemas/ButtonSchema.d.ts.map +1 -0
- package/dist/schemas/CardListGridSchema.d.ts +17 -0
- package/dist/schemas/CardListGridSchema.d.ts.map +1 -0
- package/dist/schemas/ChoiceInputFieldSchema.d.ts +18 -0
- package/dist/schemas/ChoiceInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/CodeSchema.d.ts +18 -0
- package/dist/schemas/CodeSchema.d.ts.map +1 -0
- package/dist/schemas/ContentSchema.d.ts +20 -0
- package/dist/schemas/ContentSchema.d.ts.map +1 -0
- package/dist/schemas/CoverImageHeaderSchema.d.ts +28 -0
- package/dist/schemas/CoverImageHeaderSchema.d.ts.map +1 -0
- package/dist/schemas/FeatureCardSchema.d.ts +28 -0
- package/dist/schemas/FeatureCardSchema.d.ts.map +1 -0
- package/dist/schemas/FeatureGridSchema.d.ts +17 -0
- package/dist/schemas/FeatureGridSchema.d.ts.map +1 -0
- package/dist/schemas/FeatureItemSchema.d.ts +16 -0
- package/dist/schemas/FeatureItemSchema.d.ts.map +1 -0
- package/dist/schemas/FooterItemSchema.d.ts +15 -0
- package/dist/schemas/FooterItemSchema.d.ts.map +1 -0
- package/dist/schemas/FooterSchema.d.ts +20 -0
- package/dist/schemas/FooterSchema.d.ts.map +1 -0
- package/dist/schemas/FooterSectionSchema.d.ts +15 -0
- package/dist/schemas/FooterSectionSchema.d.ts.map +1 -0
- package/dist/schemas/FormBlockSchema.d.ts +19 -0
- package/dist/schemas/FormBlockSchema.d.ts.map +1 -0
- package/dist/schemas/HeaderActionSchema.d.ts +17 -0
- package/dist/schemas/HeaderActionSchema.d.ts.map +1 -0
- package/dist/schemas/HeroBlockSchema.d.ts +22 -0
- package/dist/schemas/HeroBlockSchema.d.ts.map +1 -0
- package/dist/schemas/HtmlInputFieldSchema.d.ts +18 -0
- package/dist/schemas/HtmlInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/MetadataItemSchema.d.ts +13 -0
- package/dist/schemas/MetadataItemSchema.d.ts.map +1 -0
- package/dist/schemas/PageBannerHeaderSchema.d.ts +28 -0
- package/dist/schemas/PageBannerHeaderSchema.d.ts.map +1 -0
- package/dist/schemas/PaletteSwitcherSchema.d.ts +16 -0
- package/dist/schemas/PaletteSwitcherSchema.d.ts.map +1 -0
- package/dist/schemas/ProductCardSchema.d.ts +39 -0
- package/dist/schemas/ProductCardSchema.d.ts.map +1 -0
- package/dist/schemas/SafeSpanSchema.d.ts +13 -0
- package/dist/schemas/SafeSpanSchema.d.ts.map +1 -0
- package/dist/schemas/SectionSchema.d.ts +17 -0
- package/dist/schemas/SectionSchema.d.ts.map +1 -0
- package/dist/schemas/SelectInputFieldSchema.d.ts +27 -0
- package/dist/schemas/SelectInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/TextInputFieldSchema.d.ts +22 -0
- package/dist/schemas/TextInputFieldSchema.d.ts.map +1 -0
- package/dist/schemas/ThemeSwitcherSchema.d.ts +19 -0
- package/dist/schemas/ThemeSwitcherSchema.d.ts.map +1 -0
- package/dist/schemas/index.d.ts +33 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/types.d.ts +7 -0
- package/dist/schemas/types.d.ts.map +1 -0
- package/dist/templates/TemplateResolver.d.ts +52 -0
- package/dist/templates/TemplateResolver.d.ts.map +1 -0
- package/dist/templates/index.d.ts +7 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/tests/ConsoleWarningTest.d.ts +5 -0
- package/dist/tests/ConsoleWarningTest.d.ts.map +1 -0
- package/dist/tests/StorageKeyTest.d.ts +6 -0
- package/dist/tests/StorageKeyTest.d.ts.map +1 -0
- package/dist/tests/ThemeStorageKeyTest.d.ts +6 -0
- package/dist/tests/ThemeStorageKeyTest.d.ts.map +1 -0
- package/dist/types/CacheProvider.d.ts +18 -0
- package/dist/types/CacheProvider.d.ts.map +1 -0
- package/dist/types/ContentProxy.d.ts +47 -0
- package/dist/types/ContentProxy.d.ts.map +1 -0
- package/dist/types/DataBinding.d.ts +7 -0
- package/dist/types/DataBinding.d.ts.map +1 -0
- package/dist/types/DataProvider.d.ts +7 -0
- package/dist/types/DataProvider.d.ts.map +1 -0
- package/dist/types/DataTypes.d.ts +185 -0
- package/dist/types/DataTypes.d.ts.map +1 -0
- package/dist/types/TemplateProvider.d.ts +10 -0
- package/dist/types/TemplateProvider.d.ts.map +1 -0
- package/dist/types/TemplateResolver.d.ts +23 -0
- package/dist/types/TemplateResolver.d.ts.map +1 -0
- package/dist/types/index.d.ts +81 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/breakpoints.d.ts +35 -0
- package/dist/utils/breakpoints.d.ts.map +1 -0
- package/dist/utils/customPaletteManager.d.ts +8 -0
- package/dist/utils/customPaletteManager.d.ts.map +1 -0
- package/dist/utils/dimensions.d.ts +34 -0
- package/dist/utils/dimensions.d.ts.map +1 -0
- package/dist/utils/htmlTransform.d.ts +44 -0
- package/dist/utils/htmlTransform.d.ts.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/paletteUtils.d.ts +38 -0
- package/dist/utils/paletteUtils.d.ts.map +1 -0
- package/dist/utils/persistenceUtils.d.ts +31 -0
- package/dist/utils/persistenceUtils.d.ts.map +1 -0
- package/dist/utils/reactUtils.d.ts +24 -0
- package/dist/utils/reactUtils.d.ts.map +1 -0
- package/dist/utils/spacing.d.ts +34 -0
- package/dist/utils/spacing.d.ts.map +1 -0
- package/dist/utils/themePerformanceMonitor.d.ts +32 -0
- package/dist/utils/themePerformanceMonitor.d.ts.map +1 -0
- package/dist/utils/themeUtils.d.ts +27 -0
- package/dist/utils/themeUtils.d.ts.map +1 -0
- package/package.json +141 -0
- package/src/__tests__/components/Logo.test.js +172 -0
- package/src/__tests__/contexts/DataContext.test.js +505 -0
- package/src/__tests__/contexts/PaletteContext.test.js +115 -0
- package/src/__tests__/contexts/ThemeContext.test.js +123 -0
- package/src/__tests__/utils/paletteUtils.test.js +142 -0
- package/src/__tests__/utils/themeUtils.test.js +142 -0
- package/src/components/AccessibilityChecker.tsx +264 -0
- package/src/components/Html.tsx +191 -0
- package/src/components/Logo.css +217 -0
- package/src/components/Logo.tsx +370 -0
- package/src/components/Markdown.tsx +191 -0
- package/src/components/QwickApp.css +257 -0
- package/src/components/QwickApp.tsx +157 -0
- package/src/components/QwickAppsLogo.tsx +77 -0
- package/src/components/ResponsiveMenu.css +416 -0
- package/src/components/ResponsiveMenu.tsx +310 -0
- package/src/components/SafeSpan.tsx +128 -0
- package/src/components/Scaffold.css +541 -0
- package/src/components/Scaffold.tsx +463 -0
- package/src/components/__tests__/Article.test.tsx +419 -0
- package/src/components/__tests__/Button.test.tsx +702 -0
- package/src/components/__tests__/CardListGrid.test.tsx +478 -0
- package/src/components/__tests__/ChoiceInputField.test.tsx +864 -0
- package/src/components/__tests__/Code.test.tsx +595 -0
- package/src/components/__tests__/Content.integration.test.tsx +193 -0
- package/src/components/__tests__/Content.test.tsx +504 -0
- package/src/components/__tests__/CoverImageHeader.test.tsx +456 -0
- package/src/components/__tests__/FeatureCard.integration.test.tsx +384 -0
- package/src/components/__tests__/FeatureGrid.integration.test.tsx +364 -0
- package/src/components/__tests__/FeatureGrid.test.tsx +494 -0
- package/src/components/__tests__/Footer.test.tsx +544 -0
- package/src/components/__tests__/FormBlock.test.tsx +857 -0
- package/src/components/__tests__/HeroBlock.integration.test.tsx +272 -0
- package/src/components/__tests__/HeroBlock.test.tsx +463 -0
- package/src/components/__tests__/Html.test.tsx +174 -0
- package/src/components/__tests__/HtmlInputField.test.tsx +856 -0
- package/src/components/__tests__/Markdown.test.tsx +233 -0
- package/src/components/__tests__/PageBannerHeader.test.tsx +614 -0
- package/src/components/__tests__/PaletteSwitcher.test.tsx +864 -0
- package/src/components/__tests__/ProductCard.test.tsx +377 -0
- package/src/components/__tests__/SafeSpan.integration.test.tsx +123 -0
- package/src/components/__tests__/SafeSpan.simple.test.tsx +65 -0
- package/src/components/__tests__/SafeSpan.test.tsx +388 -0
- package/src/components/__tests__/Section.integration.test.tsx +288 -0
- package/src/components/__tests__/Section.test.tsx +494 -0
- package/src/components/__tests__/SelectInputField.test.tsx +886 -0
- package/src/components/__tests__/TextInputField.test.tsx +749 -0
- package/src/components/__tests__/ThemeSwitcher.test.tsx +777 -0
- package/src/components/blocks/Article.tsx +194 -0
- package/src/components/blocks/CardListGrid.tsx +132 -0
- package/src/components/blocks/Code.tsx +313 -0
- package/src/components/blocks/Content.tsx +265 -0
- package/src/components/blocks/CoverImageHeader.css +17 -0
- package/src/components/blocks/CoverImageHeader.tsx +435 -0
- package/src/components/blocks/FeatureCard.tsx +321 -0
- package/src/components/blocks/FeatureGrid.tsx +147 -0
- package/src/components/blocks/Footer.tsx +343 -0
- package/src/components/blocks/HeroBlock.tsx +280 -0
- package/src/components/blocks/PageBannerHeader.tsx +471 -0
- package/src/components/blocks/ProductCard.tsx +472 -0
- package/src/components/blocks/Section.tsx +209 -0
- package/src/components/blocks/index.ts +37 -0
- package/src/components/buttons/Button.tsx +233 -0
- package/src/components/buttons/PaletteSwitcher.tsx +268 -0
- package/src/components/buttons/ThemeSwitcher.tsx +283 -0
- package/src/components/buttons/index.ts +11 -0
- package/src/components/forms/FormBlock.tsx +291 -0
- package/src/components/forms/index.ts +7 -0
- package/src/components/index.ts +37 -0
- package/src/components/input/ChoiceInputField.tsx +188 -0
- package/src/components/input/HtmlInputField.tsx +326 -0
- package/src/components/input/SelectInputField.tsx +197 -0
- package/src/components/input/TextField.tsx +47 -0
- package/src/components/input/TextInputField.tsx +144 -0
- package/src/components/input/index.ts +17 -0
- package/src/components/layout/GridCell.tsx +46 -0
- package/src/components/layout/GridCellWrapper.tsx +87 -0
- package/src/components/layout/GridLayout.tsx +169 -0
- package/src/components/layout/index.ts +13 -0
- package/src/components/menu/Menu.tsx +0 -0
- package/src/components/menu/MenuItem.tsx +32 -0
- package/src/components/menu/index.ts +6 -0
- package/src/components/pages/FormPage.tsx +108 -0
- package/src/components/pages/Page.css +460 -0
- package/src/components/pages/Page.tsx +345 -0
- package/src/components/pages/index.ts +11 -0
- package/src/contexts/DataContext.tsx +355 -0
- package/src/contexts/DimensionsContext.tsx +154 -0
- package/src/contexts/PaletteContext.tsx +217 -0
- package/src/contexts/QwickAppContext.tsx +95 -0
- package/src/contexts/ThemeContext.tsx +376 -0
- package/src/contexts/index.ts +9 -0
- package/src/hooks/__tests__/useDataBinding.test.tsx.disabled +229 -0
- package/src/hooks/index.ts +11 -0
- package/src/hooks/useBaseProps.ts +267 -0
- package/src/hooks/useDataBinding.ts +77 -0
- package/src/index.ts +23 -0
- package/src/palettes/PaletteAutumn.css +172 -0
- package/src/palettes/PaletteAutumn.ts +16 -0
- package/src/palettes/PaletteCosmic.css +172 -0
- package/src/palettes/PaletteCosmic.ts +16 -0
- package/src/palettes/PaletteDefault.css +178 -0
- package/src/palettes/PaletteDefault.ts +17 -0
- package/src/palettes/PaletteOcean.css +172 -0
- package/src/palettes/PaletteOcean.ts +16 -0
- package/src/palettes/PaletteSpring.css +160 -0
- package/src/palettes/PaletteSpring.ts +16 -0
- package/src/palettes/PaletteWinter.css +172 -0
- package/src/palettes/PaletteWinter.ts +16 -0
- package/src/palettes/index.css +12 -0
- package/src/palettes/index.ts +29 -0
- package/src/schemas/ActionSchema.ts +140 -0
- package/src/schemas/ArticleSchema.ts +35 -0
- package/src/schemas/ButtonSchema.ts +99 -0
- package/src/schemas/CardListGridSchema.ts +102 -0
- package/src/schemas/ChoiceInputFieldSchema.ts +89 -0
- package/src/schemas/CodeSchema.ts +88 -0
- package/src/schemas/ContentSchema.ts +128 -0
- package/src/schemas/CoverImageHeaderSchema.ts +208 -0
- package/src/schemas/FeatureCardSchema.ts +161 -0
- package/src/schemas/FeatureGridSchema.ts +87 -0
- package/src/schemas/FeatureItemSchema.ts +68 -0
- package/src/schemas/FooterItemSchema.ts +57 -0
- package/src/schemas/FooterSchema.ts +116 -0
- package/src/schemas/FooterSectionSchema.ts +50 -0
- package/src/schemas/FormBlockSchema.ts +102 -0
- package/src/schemas/HeaderActionSchema.ts +83 -0
- package/src/schemas/HeroBlockSchema.ts +149 -0
- package/src/schemas/HtmlInputFieldSchema.ts +88 -0
- package/src/schemas/MetadataItemSchema.ts +35 -0
- package/src/schemas/PageBannerHeaderSchema.ts +206 -0
- package/src/schemas/PaletteSwitcherSchema.ts +66 -0
- package/src/schemas/ProductCardSchema.ts +264 -0
- package/src/schemas/SafeSpanSchema.ts +36 -0
- package/src/schemas/SectionSchema.ts +106 -0
- package/src/schemas/SelectInputFieldSchema.ts +137 -0
- package/src/schemas/TextInputFieldSchema.ts +129 -0
- package/src/schemas/ThemeSwitcherSchema.ts +97 -0
- package/src/schemas/__tests__/builders.test.ts +313 -0
- package/src/schemas/index.ts +34 -0
- package/src/setupTests.js +60 -0
- package/src/stories/Article.stories.tsx +549 -0
- package/src/stories/Button.stories.tsx +498 -0
- package/src/stories/CardListGrid.stories.tsx +539 -0
- package/src/stories/ChoiceInputField.stories.tsx +591 -0
- package/src/stories/Code.stories.tsx +711 -0
- package/src/stories/Content.stories.tsx +463 -0
- package/src/stories/CoverImageHeader.stories.tsx +794 -0
- package/src/stories/DataBinding.advanced.stories.tsx +548 -0
- package/src/stories/DataBinding.stories.tsx +452 -0
- package/src/stories/DataProvider.stories.tsx +1361 -0
- package/src/stories/FeatureCard.stories.tsx +642 -0
- package/src/stories/FeatureGrid.stories.tsx +669 -0
- package/src/stories/Footer.stories.tsx +724 -0
- package/src/stories/FormBlock.stories.tsx +834 -0
- package/src/stories/HeroBlock.stories.tsx +442 -0
- package/src/stories/Html.stories.tsx +264 -0
- package/src/stories/HtmlInputField.stories.tsx +558 -0
- package/src/stories/Introduction.stories.tsx +721 -0
- package/src/stories/LayoutBlocks.stories.tsx +382 -0
- package/src/stories/LayoutSystem.stories.tsx +253 -0
- package/src/stories/Logo.stories.tsx +400 -0
- package/src/stories/Markdown.stories.tsx +349 -0
- package/src/stories/Page.stories.tsx +762 -0
- package/src/stories/PageBannerHeader.stories.tsx +949 -0
- package/src/stories/PaletteSwitcher.stories.tsx +156 -0
- package/src/stories/ProductCard.stories.tsx +504 -0
- package/src/stories/QwickApp.stories.tsx +461 -0
- package/src/stories/ResponsiveMenu.stories.tsx +299 -0
- package/src/stories/SafeSpan.stories.tsx +612 -0
- package/src/stories/Section.stories.tsx +613 -0
- package/src/stories/SelectInputField.stories.tsx +605 -0
- package/src/stories/TextInputField.stories.tsx +526 -0
- package/src/stories/ThemeSwitcher.stories.tsx +170 -0
- package/src/stories/form/FormComponents.stories.tsx +588 -0
- package/src/templates/TemplateResolver.ts +156 -0
- package/src/templates/index.ts +6 -0
- package/src/tests/ConsoleWarningTest.tsx +30 -0
- package/src/tests/StorageKeyTest.tsx +110 -0
- package/src/tests/ThemeStorageKeyTest.tsx +114 -0
- package/src/types/CacheProvider.ts +14 -0
- package/src/types/ContentProxy.ts +99 -0
- package/src/types/DataTypes.ts +196 -0
- package/src/types/TemplateProvider.ts +9 -0
- package/src/types/TemplateResolver.ts +26 -0
- package/src/types/index.ts +99 -0
- package/src/utils/__tests__/createDataDrivenComponent.test.tsx.disabled +193 -0
- package/src/utils/__tests__/htmlTransform.test.tsx +255 -0
- package/src/utils/breakpoints.ts +87 -0
- package/src/utils/customPaletteManager.js +214 -0
- package/src/utils/dimensions.ts +147 -0
- package/src/utils/htmlTransform.tsx +323 -0
- package/src/utils/index.ts +16 -0
- package/src/utils/logger.ts +28 -0
- package/src/utils/paletteUtils.ts +78 -0
- package/src/utils/persistenceUtils.ts +107 -0
- package/src/utils/reactUtils.tsx +37 -0
- package/src/utils/spacing.ts +155 -0
- package/src/utils/themePerformanceMonitor.js +113 -0
- package/src/utils/themeUtils.ts +67 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Component - Main content wrapper for application pages
|
|
3
|
+
*
|
|
4
|
+
* The Page component is what developers primarily focus on when building applications.
|
|
5
|
+
* It provides:
|
|
6
|
+
* - Automatic page title management
|
|
7
|
+
* - Route-based configuration
|
|
8
|
+
* - Contextual menu items that appear in navigation
|
|
9
|
+
* - Proper content spacing and layout
|
|
10
|
+
* - SEO and accessibility features
|
|
11
|
+
*
|
|
12
|
+
* Page components automatically integrate with the AppScaffold to provide
|
|
13
|
+
* contextual actions and menu items specific to the current page.
|
|
14
|
+
*
|
|
15
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import React, { useContext, createContext, useCallback } from 'react';
|
|
19
|
+
import './Page.css';
|
|
20
|
+
import { useSafeLocation } from '../../utils/reactUtils';
|
|
21
|
+
|
|
22
|
+
export interface PageContextValue {
|
|
23
|
+
/** Current page route/path */
|
|
24
|
+
route?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Create context for page-level state
|
|
28
|
+
const PageContext = createContext<PageContextValue>({});
|
|
29
|
+
|
|
30
|
+
export const usePageContext = (): PageContextValue => {
|
|
31
|
+
const context = useContext(PageContext);
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error('usePageContext must be used within a PageProvider or Page component');
|
|
34
|
+
}
|
|
35
|
+
return context;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type MessageType = 'loading' | 'error' | 'warning' | 'success' | 'info';
|
|
39
|
+
|
|
40
|
+
export type LoadingType = 'spinner' | 'bar-top' | 'bar-bottom' | 'dots' | 'custom';
|
|
41
|
+
|
|
42
|
+
export interface MessageState {
|
|
43
|
+
/** Type of message to display */
|
|
44
|
+
type: MessageType;
|
|
45
|
+
/** Message content (string or custom JSX) */
|
|
46
|
+
content?: string | React.ReactNode;
|
|
47
|
+
/** Custom component to render instead of default */
|
|
48
|
+
customComponent?: React.ReactNode;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface LoadingState {
|
|
52
|
+
/** Type of loading indicator */
|
|
53
|
+
type?: LoadingType;
|
|
54
|
+
/** Loading message */
|
|
55
|
+
message?: string;
|
|
56
|
+
/** Custom loading component */
|
|
57
|
+
customComponent?: React.ReactNode;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface PageProps {
|
|
61
|
+
/** Page content */
|
|
62
|
+
children: React.ReactNode;
|
|
63
|
+
/** Current route/path for this page */
|
|
64
|
+
route?: string;
|
|
65
|
+
/** Optional header component */
|
|
66
|
+
header?: React.ReactNode;
|
|
67
|
+
/** Optional footer component */
|
|
68
|
+
footer?: React.ReactNode;
|
|
69
|
+
/** Additional CSS class */
|
|
70
|
+
className?: string;
|
|
71
|
+
/** Page layout variant */
|
|
72
|
+
variant?: 'default' | 'centered' | 'narrow' | 'wide' | 'fullwidth';
|
|
73
|
+
/** Padding around page content */
|
|
74
|
+
padding?: 'none' | 'small' | 'medium' | 'large';
|
|
75
|
+
/** Background variant */
|
|
76
|
+
background?: 'default' | 'surface' | 'alternate';
|
|
77
|
+
/** Maximum width for content */
|
|
78
|
+
maxWidth?: 'small' | 'medium' | 'large' | 'extra-large' | 'none';
|
|
79
|
+
/** Loading state configuration */
|
|
80
|
+
loading?: boolean | LoadingState;
|
|
81
|
+
/** Message state for displaying info/warning/error/success messages */
|
|
82
|
+
message?: MessageState;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const Page: React.FC<PageProps> = ({
|
|
86
|
+
children,
|
|
87
|
+
route,
|
|
88
|
+
header,
|
|
89
|
+
footer,
|
|
90
|
+
className = '',
|
|
91
|
+
variant = 'default',
|
|
92
|
+
padding = 'medium',
|
|
93
|
+
background = 'default',
|
|
94
|
+
maxWidth = 'large',
|
|
95
|
+
loading = false,
|
|
96
|
+
message,
|
|
97
|
+
}) => {
|
|
98
|
+
// Auto-detect current route if available
|
|
99
|
+
const location = useSafeLocation();
|
|
100
|
+
const actualRoute = route || location?.pathname || '';
|
|
101
|
+
|
|
102
|
+
// SCROLL HANDLING COMMENTED OUT FOR STABILITY
|
|
103
|
+
// Handle scroll for title shrinking
|
|
104
|
+
// useEffect(() => {
|
|
105
|
+
// if (!shrinkTitleOnScroll || !pageContentRef.current) return;
|
|
106
|
+
//
|
|
107
|
+
// const handleScroll = () => {
|
|
108
|
+
// if (pageContentRef.current) {
|
|
109
|
+
// const scrollTop = pageContentRef.current.scrollTop;
|
|
110
|
+
// const shouldCollapse = scrollTop > scrollThreshold;
|
|
111
|
+
// setIsTitleCollapsed(shouldCollapse);
|
|
112
|
+
// }
|
|
113
|
+
// };
|
|
114
|
+
//
|
|
115
|
+
// const pageElement = pageContentRef.current;
|
|
116
|
+
// if (pageElement) {
|
|
117
|
+
// pageElement.addEventListener('scroll', handleScroll);
|
|
118
|
+
// return () => pageElement.removeEventListener('scroll', handleScroll);
|
|
119
|
+
// }
|
|
120
|
+
// }, [shrinkTitleOnScroll, scrollThreshold]);
|
|
121
|
+
|
|
122
|
+
// Memoized context value
|
|
123
|
+
const contextValue = React.useMemo<PageContextValue>(() => ({
|
|
124
|
+
route: actualRoute,
|
|
125
|
+
}), [actualRoute]);
|
|
126
|
+
|
|
127
|
+
// Generate class names
|
|
128
|
+
const pageClasses = [
|
|
129
|
+
'page',
|
|
130
|
+
`page-variant-${variant}`,
|
|
131
|
+
`page-padding-${padding}`,
|
|
132
|
+
`page-background-${background}`,
|
|
133
|
+
`page-max-width-${maxWidth}`,
|
|
134
|
+
className,
|
|
135
|
+
].filter(Boolean).join(' ');
|
|
136
|
+
|
|
137
|
+
// Render loading indicator
|
|
138
|
+
const renderLoading = (loadingState: boolean | LoadingState) => {
|
|
139
|
+
if (loadingState === false) return null;
|
|
140
|
+
|
|
141
|
+
// Handle simple boolean loading
|
|
142
|
+
if (loadingState === true) {
|
|
143
|
+
return (
|
|
144
|
+
<div className="page-loading">
|
|
145
|
+
<div className="loading-spinner" aria-label="Loading content">
|
|
146
|
+
<div className="spinner"></div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle custom loading state
|
|
153
|
+
const { type = 'spinner', message, customComponent } = loadingState;
|
|
154
|
+
|
|
155
|
+
if (customComponent) {
|
|
156
|
+
return <div className="page-loading">{customComponent}</div>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
switch (type) {
|
|
160
|
+
case 'bar-top':
|
|
161
|
+
return (
|
|
162
|
+
<div className="page-loading-bar page-loading-bar-top">
|
|
163
|
+
<div className="loading-bar-progress"></div>
|
|
164
|
+
{message && <div className="loading-message">{message}</div>}
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
case 'bar-bottom':
|
|
168
|
+
return (
|
|
169
|
+
<div className="page-loading-bar page-loading-bar-bottom">
|
|
170
|
+
<div className="loading-bar-progress"></div>
|
|
171
|
+
{message && <div className="loading-message">{message}</div>}
|
|
172
|
+
</div>
|
|
173
|
+
);
|
|
174
|
+
case 'dots':
|
|
175
|
+
return (
|
|
176
|
+
<div className="page-loading page-loading-dots">
|
|
177
|
+
<div className="loading-dots" aria-label="Loading content">
|
|
178
|
+
<span></span><span></span><span></span>
|
|
179
|
+
</div>
|
|
180
|
+
{message && <div className="loading-message">{message}</div>}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
case 'spinner':
|
|
184
|
+
default:
|
|
185
|
+
return (
|
|
186
|
+
<div className="page-loading">
|
|
187
|
+
<div className="loading-spinner" aria-label="Loading content">
|
|
188
|
+
<div className="spinner"></div>
|
|
189
|
+
</div>
|
|
190
|
+
{message && <div className="loading-message">{message}</div>}
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Render message (error/warning/success/info)
|
|
197
|
+
const renderMessage = (messageState: MessageState) => {
|
|
198
|
+
const { type, content, customComponent } = messageState;
|
|
199
|
+
|
|
200
|
+
if (customComponent) {
|
|
201
|
+
return <div className={`page-message page-message-${type}`}>{customComponent}</div>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const icons = {
|
|
205
|
+
error: '⚠️',
|
|
206
|
+
warning: '⚠️',
|
|
207
|
+
success: '✅',
|
|
208
|
+
info: 'ℹ️',
|
|
209
|
+
loading: '⏳'
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<div className={`page-message page-message-${type}`}>
|
|
214
|
+
<div className="message-icon" aria-hidden="true">{icons[type]}</div>
|
|
215
|
+
<div className="message-content">
|
|
216
|
+
{typeof content === 'string' ? <p>{content}</p> : content}
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Handle loading state
|
|
223
|
+
if (loading) {
|
|
224
|
+
return (
|
|
225
|
+
<PageContext.Provider value={contextValue}>
|
|
226
|
+
<div className={pageClasses}>
|
|
227
|
+
{renderLoading(loading)}
|
|
228
|
+
</div>
|
|
229
|
+
</PageContext.Provider>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<PageContext.Provider value={contextValue}>
|
|
235
|
+
<div className={pageClasses}>
|
|
236
|
+
{/* Optional Header */}
|
|
237
|
+
{header && (
|
|
238
|
+
<div className="page-header">
|
|
239
|
+
{header}
|
|
240
|
+
</div>
|
|
241
|
+
)}
|
|
242
|
+
|
|
243
|
+
{/* Message state (error/warning/success/info) */}
|
|
244
|
+
{message && renderMessage(message)}
|
|
245
|
+
|
|
246
|
+
{/* Page Content */}
|
|
247
|
+
<div className="page-content">
|
|
248
|
+
{children}
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
{/* Optional Footer */}
|
|
252
|
+
{footer && (
|
|
253
|
+
<div className="page-footer">
|
|
254
|
+
{footer}
|
|
255
|
+
</div>
|
|
256
|
+
)}
|
|
257
|
+
</div>
|
|
258
|
+
</PageContext.Provider>
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Utility hook for updating page configuration from within page components
|
|
263
|
+
export const usePage = () => {
|
|
264
|
+
const context = usePageContext();
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
...context,
|
|
268
|
+
// Helper method for setting page title
|
|
269
|
+
setTitle: useCallback((newTitle: string) => {
|
|
270
|
+
document.title = newTitle;
|
|
271
|
+
}, []),
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Base Page Class Component - Extendable page component for inheritance pattern
|
|
277
|
+
*
|
|
278
|
+
* Usage:
|
|
279
|
+
* class MyPage extends BasePage {
|
|
280
|
+
* getPageProps() {
|
|
281
|
+
* return {
|
|
282
|
+
* route: '/my-page',
|
|
283
|
+
* variant: 'narrow',
|
|
284
|
+
* padding: 'large',
|
|
285
|
+
* // ... other page props
|
|
286
|
+
* };
|
|
287
|
+
* }
|
|
288
|
+
*
|
|
289
|
+
* renderContent() {
|
|
290
|
+
* return (
|
|
291
|
+
* <div>My page content</div>
|
|
292
|
+
* );
|
|
293
|
+
* }
|
|
294
|
+
* }
|
|
295
|
+
*/
|
|
296
|
+
export abstract class BasePage<P = {}, S = {}> extends React.Component<P, S> {
|
|
297
|
+
/**
|
|
298
|
+
* Override this method to provide page configuration
|
|
299
|
+
* This will be passed to the Page component wrapper
|
|
300
|
+
*/
|
|
301
|
+
abstract getPageProps(): Partial<PageProps>;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Override this method to provide the page content
|
|
305
|
+
* This replaces the render() method in your page components
|
|
306
|
+
*/
|
|
307
|
+
abstract renderContent(): React.ReactNode;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Override this method to handle page mounting logic
|
|
311
|
+
* Called after the page is mounted and context is available
|
|
312
|
+
*/
|
|
313
|
+
onPageMount?(): void;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Override this method to handle page unmounting logic
|
|
317
|
+
*/
|
|
318
|
+
onPageUnmount?(): void;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Internal render method - wraps content in Page component
|
|
322
|
+
*/
|
|
323
|
+
render() {
|
|
324
|
+
const pageProps = this.getPageProps();
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<Page {...pageProps}>
|
|
328
|
+
{this.renderContent()}
|
|
329
|
+
</Page>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Component lifecycle methods
|
|
335
|
+
*/
|
|
336
|
+
componentDidMount() {
|
|
337
|
+
this.onPageMount?.();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
componentWillUnmount() {
|
|
341
|
+
this.onPageUnmount?.();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export default Page;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All page components available in QwickApps React Framework.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
export { BasePage } from './Page';
|
|
7
|
+
export { default as FormPage } from './FormPage';
|
|
8
|
+
export { default as Page } from './Page';
|
|
9
|
+
|
|
10
|
+
export type { FormPageProps } from './FormPage';
|
|
11
|
+
export type { PageProps } from './Page';
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataContext - React Context for Data Provider
|
|
3
|
+
*
|
|
4
|
+
* Provides data templating capabilities throughout the React component tree
|
|
5
|
+
* via React Context API. Supports mustache template resolution and caching.
|
|
6
|
+
* Enables JSX components to use mustache syntax to reference named data elements.
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
|
12
|
+
import { ITemplateResolver, TemplateResolverConfig } from '../types';
|
|
13
|
+
import { TemplateResolver } from '../templates';
|
|
14
|
+
import { Model } from '@qwickapps/schema';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Data Context interface
|
|
18
|
+
*/
|
|
19
|
+
interface DataContextValue {
|
|
20
|
+
provider: ITemplateResolver | null;
|
|
21
|
+
get<T extends Model>(slug: string): Promise<T | undefined>;
|
|
22
|
+
select<T extends Model>(schema: string, options?: any): Promise<T[]>;
|
|
23
|
+
resolveTemplate(template: string): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* React Context for data provider
|
|
28
|
+
*/
|
|
29
|
+
const DataContext = createContext<DataContextValue | null>(null);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Props for DataProvider component
|
|
33
|
+
*/
|
|
34
|
+
export interface DataProviderProps {
|
|
35
|
+
/** The data source configuration (follows new SRP architecture) */
|
|
36
|
+
dataSource: TemplateResolverConfig;
|
|
37
|
+
/** Child components that will have access to data templating */
|
|
38
|
+
children: ReactNode;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* DataProvider component that wraps children with data templating capabilities
|
|
43
|
+
*
|
|
44
|
+
* Enables JSX components to use mustache syntax for data interpolation:
|
|
45
|
+
* - {t`Welcome {{company.name}}!`}
|
|
46
|
+
* - {useData('users')} for data fetching
|
|
47
|
+
* - Template resolution with caching support
|
|
48
|
+
*
|
|
49
|
+
* Usage (new SRP architecture):
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <DataProvider dataSource={{
|
|
52
|
+
* dataProvider: new JsonDataProvider({ data: { ... } }),
|
|
53
|
+
* cache: true,
|
|
54
|
+
* templateResolver: new MustacheTemplateResolver()
|
|
55
|
+
* }}>
|
|
56
|
+
* <MyComponent /> // Can use useData() hooks and t`` templates
|
|
57
|
+
* </DataProvider>
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function DataProvider({ dataSource, children }: DataProviderProps) {
|
|
61
|
+
// Create TemplateResolver internally
|
|
62
|
+
const dataProvider = new TemplateResolver(dataSource);
|
|
63
|
+
|
|
64
|
+
const contextValue: DataContextValue = {
|
|
65
|
+
provider: dataProvider,
|
|
66
|
+
get: async <T extends Model>(slug: string): Promise<T | undefined> => {
|
|
67
|
+
const result = await dataProvider.get(slug);
|
|
68
|
+
return result && result.data ? (result.data as T) : undefined;
|
|
69
|
+
},
|
|
70
|
+
select: async <T extends Model>(schema: string, options?: any): Promise<T[]> => {
|
|
71
|
+
const result = await dataProvider.select(schema, options);
|
|
72
|
+
return result && Array.isArray(result.data) ? (result.data as T[]) : [];
|
|
73
|
+
},
|
|
74
|
+
resolveTemplate: (template: string) => dataProvider.resolveTemplate(template)
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<DataContext.Provider value={contextValue}>
|
|
79
|
+
{children}
|
|
80
|
+
</DataContext.Provider>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Hook to access the data context
|
|
86
|
+
*
|
|
87
|
+
* @throws Error if used outside of DataProvider
|
|
88
|
+
*/
|
|
89
|
+
export function useDataContext(): DataContextValue {
|
|
90
|
+
const context = useContext(DataContext);
|
|
91
|
+
if (!context) {
|
|
92
|
+
throw new Error('useDataContext must be used within a DataProvider. Did you wrap your component with <DataProvider>?');
|
|
93
|
+
}
|
|
94
|
+
return context;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Hook to get data by unique slug (safe version that doesn't throw)
|
|
99
|
+
*
|
|
100
|
+
* @param slug - The unique slug to fetch data for
|
|
101
|
+
* @returns Data with loading state information
|
|
102
|
+
*/
|
|
103
|
+
export function useDataSafe<T extends Model>(slug: string): {
|
|
104
|
+
data: T | undefined;
|
|
105
|
+
loading: boolean;
|
|
106
|
+
error: Error | null;
|
|
107
|
+
} {
|
|
108
|
+
const context = useContext(DataContext);
|
|
109
|
+
if (!context) {
|
|
110
|
+
return { data: undefined, loading: false, error: null };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const [data, setData] = useState<T | undefined>(undefined);
|
|
114
|
+
const [loading, setLoading] = useState(true);
|
|
115
|
+
const [error, setError] = useState<Error | null>(null);
|
|
116
|
+
|
|
117
|
+
const fetchData = async () => {
|
|
118
|
+
if (!slug) {
|
|
119
|
+
setLoading(false);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
setLoading(true);
|
|
125
|
+
setError(null);
|
|
126
|
+
const result = await context.get<T>(slug);
|
|
127
|
+
setData(result);
|
|
128
|
+
setLoading(false);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
131
|
+
setLoading(false);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
fetchData();
|
|
137
|
+
}, [slug]);
|
|
138
|
+
|
|
139
|
+
return { data, loading, error };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Hook to get data by field group ID
|
|
144
|
+
*
|
|
145
|
+
* @param fieldGroupId - The field group to fetch
|
|
146
|
+
* @returns Data and loading state
|
|
147
|
+
*/
|
|
148
|
+
export function useData<T extends Model>(fieldGroupId: string): {
|
|
149
|
+
data: T | undefined;
|
|
150
|
+
loading: boolean;
|
|
151
|
+
error: Error | null;
|
|
152
|
+
refetch: () => void;
|
|
153
|
+
} {
|
|
154
|
+
const { get } = useDataContext();
|
|
155
|
+
const [data, setData] = useState<T | undefined>(undefined);
|
|
156
|
+
const [loading, setLoading] = useState(true);
|
|
157
|
+
const [error, setError] = useState<Error | null>(null);
|
|
158
|
+
|
|
159
|
+
const fetchData = async () => {
|
|
160
|
+
if (!fieldGroupId) {
|
|
161
|
+
setLoading(false);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
setLoading(true);
|
|
167
|
+
setError(null);
|
|
168
|
+
const result = await get<T>(fieldGroupId);
|
|
169
|
+
setData(result ? result : undefined);
|
|
170
|
+
setLoading(false);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
173
|
+
setLoading(false);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
fetchData();
|
|
179
|
+
}, [fieldGroupId, get]);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
data,
|
|
183
|
+
loading,
|
|
184
|
+
error,
|
|
185
|
+
refetch: fetchData
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Hook to resolve a template string with mustache syntax
|
|
191
|
+
*
|
|
192
|
+
* @param template - Template string with {{fieldGroup.property}} syntax
|
|
193
|
+
* @returns Resolved template string and loading state
|
|
194
|
+
*/
|
|
195
|
+
export function useResolveTemplate(template: string) {
|
|
196
|
+
const { resolveTemplate } = useDataContext();
|
|
197
|
+
const [resolved, setResolved] = useState(template);
|
|
198
|
+
const [loading, setLoading] = useState(false);
|
|
199
|
+
const [error, setError] = useState<Error | null>(null);
|
|
200
|
+
|
|
201
|
+
useEffect(() => {
|
|
202
|
+
let isMounted = true;
|
|
203
|
+
|
|
204
|
+
async function resolve() {
|
|
205
|
+
if (!template || !template.includes('{{')) {
|
|
206
|
+
setResolved(template);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
setLoading(true);
|
|
212
|
+
setError(null);
|
|
213
|
+
const result = await resolveTemplate(template);
|
|
214
|
+
|
|
215
|
+
if (isMounted) {
|
|
216
|
+
setResolved(result);
|
|
217
|
+
setLoading(false);
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
if (isMounted) {
|
|
221
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
222
|
+
setResolved(template); // Fallback to original template
|
|
223
|
+
setLoading(false);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
resolve();
|
|
229
|
+
|
|
230
|
+
return () => {
|
|
231
|
+
isMounted = false;
|
|
232
|
+
};
|
|
233
|
+
}, [template, resolveTemplate]);
|
|
234
|
+
|
|
235
|
+
return { resolved, loading, error };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Hook to access data provider directly for advanced use cases
|
|
240
|
+
*/
|
|
241
|
+
export function useDataProvider(): ITemplateResolver {
|
|
242
|
+
const { provider } = useDataContext();
|
|
243
|
+
if (!provider) {
|
|
244
|
+
throw new Error('Data provider not available');
|
|
245
|
+
}
|
|
246
|
+
return provider;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Template component for inline template resolution
|
|
251
|
+
*
|
|
252
|
+
* This is a React-friendly component for template resolution
|
|
253
|
+
* that works well with conditional rendering and fallbacks.
|
|
254
|
+
*
|
|
255
|
+
* Usage:
|
|
256
|
+
* ```tsx
|
|
257
|
+
* <T template="Welcome to {{company.name}}!" fallback="Loading..." />
|
|
258
|
+
* <T template="Hello {{user.name}}" fallback="Guest" />
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
export const T: React.FC<{ template: string; fallback?: ReactNode; wrapper?: React.ComponentType<{ children: ReactNode }> }> = ({
|
|
262
|
+
template,
|
|
263
|
+
fallback,
|
|
264
|
+
wrapper: Wrapper
|
|
265
|
+
}) => {
|
|
266
|
+
try {
|
|
267
|
+
const context = useContext(DataContext);
|
|
268
|
+
if (!context) {
|
|
269
|
+
throw new Error('T component must be used within a DataProvider. Did you wrap your component with <DataProvider>?');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const { resolved, loading, error } = useResolveTemplate(template);
|
|
273
|
+
|
|
274
|
+
if (loading) {
|
|
275
|
+
return fallback ? <>{fallback}</> : null;
|
|
276
|
+
} else if (error) {
|
|
277
|
+
if (process.env.NODE_ENV === 'development') {
|
|
278
|
+
return <>[Template Error: {error.message}]</>;
|
|
279
|
+
} else {
|
|
280
|
+
return fallback ? <>{fallback}</> : null;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
const content = <>{resolved}</>;
|
|
284
|
+
return Wrapper ? <Wrapper>{content}</Wrapper> : content;
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Hook to resolve template with tagged template literal syntax
|
|
293
|
+
*
|
|
294
|
+
* This returns the resolved value directly so it can be used with || operator
|
|
295
|
+
*/
|
|
296
|
+
export function useTemplate(template: string): string | null {
|
|
297
|
+
const { resolved, loading, error } = useResolveTemplate(template);
|
|
298
|
+
|
|
299
|
+
if (loading) {
|
|
300
|
+
return null;
|
|
301
|
+
} else if (error) {
|
|
302
|
+
if (process.env.NODE_ENV === 'development') {
|
|
303
|
+
return `[Template Error: ${error.message}]`;
|
|
304
|
+
} else {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
} else {
|
|
308
|
+
return resolved;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Tagged template function for inline data templating
|
|
314
|
+
*
|
|
315
|
+
* Enables JSX components to use mustache syntax for data interpolation:
|
|
316
|
+
*
|
|
317
|
+
* Usage:
|
|
318
|
+
* ```tsx
|
|
319
|
+
* {t`Welcome to {{company.name}}!`}
|
|
320
|
+
* {t`Hello {{user.name}}` || 'Loading...'}
|
|
321
|
+
* {t.wrap(CustomComponent)`{{company.slogan}}`}
|
|
322
|
+
* ```
|
|
323
|
+
*/
|
|
324
|
+
export function t(strings: TemplateStringsArray, ...values: any[]): ReactNode {
|
|
325
|
+
// First do regular template literal interpolation
|
|
326
|
+
const template = String.raw(strings, ...values);
|
|
327
|
+
|
|
328
|
+
// Return a component that uses the hook
|
|
329
|
+
const TemplateResolver = () => {
|
|
330
|
+
const resolved = useTemplate(template);
|
|
331
|
+
return resolved;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
return <TemplateResolver />;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Tagged template function with custom wrapper component
|
|
339
|
+
*
|
|
340
|
+
* Usage:
|
|
341
|
+
* ```tsx
|
|
342
|
+
* {t.wrap(QuoteComponent)`{{company.slogan}}`}
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
t.wrap = (WrapperComponent: React.ComponentType<{ children: ReactNode }>) =>
|
|
346
|
+
(strings: TemplateStringsArray, ...values: any[]): ReactNode => {
|
|
347
|
+
const template = String.raw(strings, ...values);
|
|
348
|
+
|
|
349
|
+
const WrappedTemplate = () => {
|
|
350
|
+
const resolved = useTemplate(template);
|
|
351
|
+
return resolved ? <WrapperComponent>{resolved}</WrapperComponent> : null;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
return <WrappedTemplate />;
|
|
355
|
+
};
|