@qwickapps/react-framework 1.3.4 → 1.3.5

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 CHANGED
@@ -4,6 +4,13 @@ A complete React framework for building modern, responsive applications with int
4
4
 
5
5
  ## What's New
6
6
 
7
+ ### September 4, 2025 - Stability & Inline Wrapper Guard
8
+
9
+ - **ProductCard Stability Fix**: Removed inline wrapper React components that caused subtree remounts and potential focus/state loss; replaced with stable JSX fragments.
10
+ - **Preventive Lint Rule**: Repo now enforces a custom `no-inline-component-wrapper` ESLint rule (scoped rollout) to block reintroduction of the remount pattern.
11
+ - **Internal Refactor Only**: No public API changes; safe patch update.
12
+
13
+
7
14
  ### September 2, 2025 - Built-in Error Handling & Accessibility
8
15
  - **Automatic Error Boundaries**: QwickApp now automatically wraps all content with ErrorBoundary for robust error handling
9
16
  - **Built-in Accessibility**: AccessibilityProvider automatically included for WCAG 2.1 AA compliance
@@ -1 +1 @@
1
- {"version":3,"file":"Content.d.ts","sourceRoot":"","sources":["../../../src/components/blocks/Content.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGhE,OAAO,YAAY,MAAM,6BAA6B,CAAC;AAMvD,KAAK,gBAAgB,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;AAEjD,MAAM,WAAW,YAAa,SAAQ,gBAAgB,EAAE,eAAe;CAAG;AA4L1E,iBAAS,OAAO,CAAC,KAAK,EAAE,YAAY,kDA6CnC;AAED,eAAe,OAAO,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"Content.d.ts","sourceRoot":"","sources":["../../../src/components/blocks/Content.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGhE,OAAO,YAAY,MAAM,6BAA6B,CAAC;AAMvD,KAAK,gBAAgB,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;AAEjD,MAAM,WAAW,YAAa,SAAQ,gBAAgB,EAAE,eAAe;CAAG;AAwI1E,iBAAS,OAAO,CAAC,KAAK,EAAE,YAAY,kDA6CnC;AAED,eAAe,OAAO,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductCard.d.ts","sourceRoot":"","sources":["../../../src/components/blocks/ProductCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAoD,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9F,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5C,KAAK,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,UAAU,oBAAqB,SAAQ,aAAa;IAClD,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,EAAE,eAAe;CAAG;AAgVlF;;;GAGG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDAmE3C;AAED,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"ProductCard.d.ts","sourceRoot":"","sources":["../../../src/components/blocks/ProductCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAoD,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9F,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IAC5C,KAAK,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,UAAU,oBAAqB,SAAQ,aAAa;IAClD,mBAAmB;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,CAAC;IACjC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC9B,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gCAAgC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB,EAAE,eAAe;CAAG;AA+UlF;;;GAGG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDAmE3C;AAED,eAAe,WAAW,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ThemeContext.d.ts","sourceRoot":"","sources":["../../src/contexts/ThemeContext.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAkD,SAAS,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAe,KAAK,EAAqC,MAAM,sBAAsB,CAAC;AAW7F,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,YAAY,EAAE,SAAS,CAAC;IACxB,6DAA6D;IAC7D,cAAc,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,yEAAyE;IACzE,eAAe,EAAE,eAAe,CAAC;IACjC,2BAA2B;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,YAAY,EAAE,OAAO,CAAC;IAGtB,uEAAuE;IACvE,iBAAiB,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC9C,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,SAAS,GAAG,IAAI,CAAC;IAG1C,kCAAkC;IAClC,eAAe,EAAE,MAAM,SAAS,CAAC;IACjC,2EAA2E;IAC3E,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAKD,eAAO,MAAM,QAAQ,QAAO,iBAM3B,CAAC;AAoBF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA0QtD,CAAC"}
1
+ {"version":3,"file":"ThemeContext.d.ts","sourceRoot":"","sources":["../../src/contexts/ThemeContext.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAkD,SAAS,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAe,KAAK,EAAqC,MAAM,sBAAsB,CAAC;AAW7F,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,YAAY,EAAE,SAAS,CAAC;IACxB,6DAA6D;IAC7D,cAAc,EAAE,SAAS,GAAG,IAAI,CAAC;IACjC,yEAAyE;IACzE,eAAe,EAAE,eAAe,CAAC;IACjC,2BAA2B;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,wDAAwD;IACxD,YAAY,EAAE,OAAO,CAAC;IAGtB,uEAAuE;IACvE,iBAAiB,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC9C,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,iCAAiC;IACjC,iBAAiB,EAAE,MAAM,SAAS,GAAG,IAAI,CAAC;IAG1C,kCAAkC;IAClC,eAAe,EAAE,MAAM,SAAS,CAAC;IACjC,2EAA2E;IAC3E,eAAe,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;IAC9B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAKD,eAAO,MAAM,QAAQ,QAAO,iBAM3B,CAAC;AAoBF,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAyQtD,CAAC"}
package/dist/index.esm.js CHANGED
@@ -13760,72 +13760,62 @@ function ContentView({
13760
13760
  return 3;
13761
13761
  }
13762
13762
  };
13763
- // Content wrapper based on variant
13764
- const ContentWrapper = ({
13765
- children
13766
- }) => {
13767
- const paddingValue = getPadding();
13768
- const mappedMaxWidth = mapToMUIBreakpoint(contentMaxWidth === 'false' ? false : contentMaxWidth);
13769
- const commonSx = {
13770
- textAlign: centered ? 'center' : 'left',
13771
- maxWidth: mappedMaxWidth !== false ? theme.breakpoints.values[mappedMaxWidth] : '100%',
13772
- width: '100%',
13773
- ...(centered && contentMaxWidth && {
13774
- mx: 'auto'
13775
- }),
13776
- p: paddingValue,
13777
- ...styleProps.sx
13778
- };
13779
- switch (variant) {
13780
- case 'elevated':
13781
- return jsx(Paper, {
13782
- elevation: 4,
13783
- ...htmlProps,
13784
- ...otherProps,
13785
- sx: {
13786
- ...commonSx,
13787
- backgroundColor: theme.palette.background.paper
13788
- },
13789
- children: children
13790
- });
13791
- case 'outlined':
13792
- return jsx(Paper, {
13793
- variant: "outlined",
13794
- elevation: 0,
13795
- ...htmlProps,
13796
- ...otherProps,
13797
- sx: {
13798
- ...commonSx,
13799
- backgroundColor: 'var(--theme-surface)',
13800
- borderColor: 'var(--theme-border-main)',
13801
- // Use theme border color
13802
- borderWidth: 1,
13803
- borderStyle: 'solid'
13804
- },
13805
- children: children
13806
- });
13807
- case 'filled':
13808
- return jsx(Box, {
13809
- ...htmlProps,
13810
- ...otherProps,
13811
- sx: {
13812
- ...commonSx,
13813
- backgroundColor: 'var(--theme-surface-variant)',
13814
- // Use theme surface variant
13815
- borderRadius: 1
13816
- },
13817
- children: children
13818
- });
13819
- default:
13820
- return jsx(Box, {
13821
- ...htmlProps,
13822
- ...otherProps,
13823
- sx: commonSx,
13824
- children: children
13825
- });
13826
- }
13763
+ // Compute stable wrapper element (avoid recreating component type each render which caused remount & focus loss)
13764
+ const paddingValue = getPadding();
13765
+ const mappedMaxWidth = mapToMUIBreakpoint(contentMaxWidth === 'false' ? false : contentMaxWidth);
13766
+ const commonSx = {
13767
+ textAlign: centered ? 'center' : 'left',
13768
+ maxWidth: mappedMaxWidth !== false ? theme.breakpoints.values[mappedMaxWidth] : '100%',
13769
+ width: '100%',
13770
+ ...(centered && contentMaxWidth && {
13771
+ mx: 'auto'
13772
+ }),
13773
+ p: paddingValue,
13774
+ ...styleProps.sx
13775
+ };
13776
+ let Wrapper = Box;
13777
+ let wrapperProps = {
13778
+ ...htmlProps,
13779
+ ...otherProps,
13780
+ sx: commonSx
13827
13781
  };
13828
- return jsx(ContentWrapper, {
13782
+ if (variant === 'elevated') {
13783
+ Wrapper = Paper;
13784
+ wrapperProps = {
13785
+ ...wrapperProps,
13786
+ elevation: 4,
13787
+ sx: {
13788
+ ...commonSx,
13789
+ backgroundColor: theme.palette.background.paper
13790
+ }
13791
+ };
13792
+ } else if (variant === 'outlined') {
13793
+ Wrapper = Paper;
13794
+ wrapperProps = {
13795
+ ...wrapperProps,
13796
+ variant: 'outlined',
13797
+ elevation: 0,
13798
+ sx: {
13799
+ ...commonSx,
13800
+ backgroundColor: 'var(--theme-surface)',
13801
+ borderColor: 'var(--theme-border-main)',
13802
+ borderWidth: 1,
13803
+ borderStyle: 'solid'
13804
+ }
13805
+ };
13806
+ } else if (variant === 'filled') {
13807
+ Wrapper = Box;
13808
+ wrapperProps = {
13809
+ ...wrapperProps,
13810
+ sx: {
13811
+ ...commonSx,
13812
+ backgroundColor: 'var(--theme-surface-variant)',
13813
+ borderRadius: 1
13814
+ }
13815
+ };
13816
+ }
13817
+ return jsx(Wrapper, {
13818
+ ...wrapperProps,
13829
13819
  children: jsxs(Stack, {
13830
13820
  spacing: 2,
13831
13821
  children: [(title || subtitle) && jsxs(Box, {
@@ -17137,7 +17127,9 @@ function ProductCardView({
17137
17127
  return actions;
17138
17128
  };
17139
17129
  const displayActions = actions || getDefaultActions();
17140
- const FeaturesList = () => {
17130
+ // INLINE WRAPPERS REFACTORED: Instead of inline component declarations (which cause remounts),
17131
+ // compute JSX fragments via plain variables/functions and inject directly.
17132
+ const featuresListElement = (() => {
17141
17133
  const featuresToShow = variant === 'compact' ? (product.features || []).slice(0, maxFeaturesCompact) : product.features || [];
17142
17134
  return jsxs(Box, {
17143
17135
  sx: {
@@ -17197,40 +17189,37 @@ function ProductCardView({
17197
17189
  })]
17198
17190
  })]
17199
17191
  });
17200
- };
17201
- const TechnologiesSection = () => {
17202
- if (!showTechnologies || variant === 'compact') return null;
17203
- return jsxs(Box, {
17192
+ })();
17193
+ const technologiesSectionElement = !showTechnologies || variant === 'compact' ? null : jsxs(Box, {
17194
+ sx: {
17195
+ mb: 3
17196
+ },
17197
+ children: [jsx(Typography, {
17198
+ variant: "h6",
17199
+ component: "h4",
17204
17200
  sx: {
17205
- mb: 3
17201
+ mb: 1.5,
17202
+ fontSize: '1rem',
17203
+ fontWeight: 600
17206
17204
  },
17207
- children: [jsx(Typography, {
17208
- variant: "h6",
17209
- component: "h4",
17210
- sx: {
17211
- mb: 1.5,
17212
- fontSize: '1rem',
17213
- fontWeight: 600
17214
- },
17215
- children: "Technologies:"
17216
- }), jsx(Box, {
17205
+ children: "Technologies:"
17206
+ }), jsx(Box, {
17207
+ sx: {
17208
+ display: 'flex',
17209
+ flexWrap: 'wrap',
17210
+ gap: 1
17211
+ },
17212
+ children: product.technologies.map((tech, index) => jsx(Chip, {
17213
+ label: tech,
17214
+ variant: "outlined",
17215
+ size: "small",
17217
17216
  sx: {
17218
- display: 'flex',
17219
- flexWrap: 'wrap',
17220
- gap: 1
17221
- },
17222
- children: product.technologies.map((tech, index) => jsx(Chip, {
17223
- label: tech,
17224
- variant: "outlined",
17225
- size: "small",
17226
- sx: {
17227
- fontSize: '0.8rem',
17228
- fontWeight: 500
17229
- }
17230
- }, index))
17231
- })]
17232
- });
17233
- };
17217
+ fontSize: '0.8rem',
17218
+ fontWeight: 500
17219
+ }
17220
+ }, index))
17221
+ })]
17222
+ });
17234
17223
  return jsxs(Box, {
17235
17224
  className: styleProps.className || "product-card",
17236
17225
  onClick: htmlProps.onClick || (variant === 'compact' ? handleProductClick : undefined),
@@ -17336,7 +17325,7 @@ function ProductCardView({
17336
17325
  },
17337
17326
  children: variant === 'detailed' ? product.description : product.shortDescription || product.description
17338
17327
  })]
17339
- }), jsx(FeaturesList, {}), jsx(TechnologiesSection, {}), jsx(Box, {
17328
+ }), featuresListElement, technologiesSectionElement, jsx(Box, {
17340
17329
  sx: {
17341
17330
  display: 'flex',
17342
17331
  gap: 1.5,
@@ -18197,7 +18186,6 @@ const ThemeProvider = ({
18197
18186
  }
18198
18187
  }
18199
18188
  });
18200
- // eslint-disable-next-line react-hooks/exhaustive-deps
18201
18189
  }, [actualThemeMode, paletteChangeCounter]); // paletteChangeCounter is needed to force theme rebuild // Re-create theme when palette changes
18202
18190
  // Theme preference management methods
18203
18191
  const setPreferredTheme = theme => {
package/dist/index.js CHANGED
@@ -13762,72 +13762,62 @@ function ContentView({
13762
13762
  return 3;
13763
13763
  }
13764
13764
  };
13765
- // Content wrapper based on variant
13766
- const ContentWrapper = ({
13767
- children
13768
- }) => {
13769
- const paddingValue = getPadding();
13770
- const mappedMaxWidth = mapToMUIBreakpoint(contentMaxWidth === 'false' ? false : contentMaxWidth);
13771
- const commonSx = {
13772
- textAlign: centered ? 'center' : 'left',
13773
- maxWidth: mappedMaxWidth !== false ? theme.breakpoints.values[mappedMaxWidth] : '100%',
13774
- width: '100%',
13775
- ...(centered && contentMaxWidth && {
13776
- mx: 'auto'
13777
- }),
13778
- p: paddingValue,
13779
- ...styleProps.sx
13780
- };
13781
- switch (variant) {
13782
- case 'elevated':
13783
- return jsxRuntime.jsx(material.Paper, {
13784
- elevation: 4,
13785
- ...htmlProps,
13786
- ...otherProps,
13787
- sx: {
13788
- ...commonSx,
13789
- backgroundColor: theme.palette.background.paper
13790
- },
13791
- children: children
13792
- });
13793
- case 'outlined':
13794
- return jsxRuntime.jsx(material.Paper, {
13795
- variant: "outlined",
13796
- elevation: 0,
13797
- ...htmlProps,
13798
- ...otherProps,
13799
- sx: {
13800
- ...commonSx,
13801
- backgroundColor: 'var(--theme-surface)',
13802
- borderColor: 'var(--theme-border-main)',
13803
- // Use theme border color
13804
- borderWidth: 1,
13805
- borderStyle: 'solid'
13806
- },
13807
- children: children
13808
- });
13809
- case 'filled':
13810
- return jsxRuntime.jsx(material.Box, {
13811
- ...htmlProps,
13812
- ...otherProps,
13813
- sx: {
13814
- ...commonSx,
13815
- backgroundColor: 'var(--theme-surface-variant)',
13816
- // Use theme surface variant
13817
- borderRadius: 1
13818
- },
13819
- children: children
13820
- });
13821
- default:
13822
- return jsxRuntime.jsx(material.Box, {
13823
- ...htmlProps,
13824
- ...otherProps,
13825
- sx: commonSx,
13826
- children: children
13827
- });
13828
- }
13765
+ // Compute stable wrapper element (avoid recreating component type each render which caused remount & focus loss)
13766
+ const paddingValue = getPadding();
13767
+ const mappedMaxWidth = mapToMUIBreakpoint(contentMaxWidth === 'false' ? false : contentMaxWidth);
13768
+ const commonSx = {
13769
+ textAlign: centered ? 'center' : 'left',
13770
+ maxWidth: mappedMaxWidth !== false ? theme.breakpoints.values[mappedMaxWidth] : '100%',
13771
+ width: '100%',
13772
+ ...(centered && contentMaxWidth && {
13773
+ mx: 'auto'
13774
+ }),
13775
+ p: paddingValue,
13776
+ ...styleProps.sx
13777
+ };
13778
+ let Wrapper = material.Box;
13779
+ let wrapperProps = {
13780
+ ...htmlProps,
13781
+ ...otherProps,
13782
+ sx: commonSx
13829
13783
  };
13830
- return jsxRuntime.jsx(ContentWrapper, {
13784
+ if (variant === 'elevated') {
13785
+ Wrapper = material.Paper;
13786
+ wrapperProps = {
13787
+ ...wrapperProps,
13788
+ elevation: 4,
13789
+ sx: {
13790
+ ...commonSx,
13791
+ backgroundColor: theme.palette.background.paper
13792
+ }
13793
+ };
13794
+ } else if (variant === 'outlined') {
13795
+ Wrapper = material.Paper;
13796
+ wrapperProps = {
13797
+ ...wrapperProps,
13798
+ variant: 'outlined',
13799
+ elevation: 0,
13800
+ sx: {
13801
+ ...commonSx,
13802
+ backgroundColor: 'var(--theme-surface)',
13803
+ borderColor: 'var(--theme-border-main)',
13804
+ borderWidth: 1,
13805
+ borderStyle: 'solid'
13806
+ }
13807
+ };
13808
+ } else if (variant === 'filled') {
13809
+ Wrapper = material.Box;
13810
+ wrapperProps = {
13811
+ ...wrapperProps,
13812
+ sx: {
13813
+ ...commonSx,
13814
+ backgroundColor: 'var(--theme-surface-variant)',
13815
+ borderRadius: 1
13816
+ }
13817
+ };
13818
+ }
13819
+ return jsxRuntime.jsx(Wrapper, {
13820
+ ...wrapperProps,
13831
13821
  children: jsxRuntime.jsxs(material.Stack, {
13832
13822
  spacing: 2,
13833
13823
  children: [(title || subtitle) && jsxRuntime.jsxs(material.Box, {
@@ -17139,7 +17129,9 @@ function ProductCardView({
17139
17129
  return actions;
17140
17130
  };
17141
17131
  const displayActions = actions || getDefaultActions();
17142
- const FeaturesList = () => {
17132
+ // INLINE WRAPPERS REFACTORED: Instead of inline component declarations (which cause remounts),
17133
+ // compute JSX fragments via plain variables/functions and inject directly.
17134
+ const featuresListElement = (() => {
17143
17135
  const featuresToShow = variant === 'compact' ? (product.features || []).slice(0, maxFeaturesCompact) : product.features || [];
17144
17136
  return jsxRuntime.jsxs(material.Box, {
17145
17137
  sx: {
@@ -17199,40 +17191,37 @@ function ProductCardView({
17199
17191
  })]
17200
17192
  })]
17201
17193
  });
17202
- };
17203
- const TechnologiesSection = () => {
17204
- if (!showTechnologies || variant === 'compact') return null;
17205
- return jsxRuntime.jsxs(material.Box, {
17194
+ })();
17195
+ const technologiesSectionElement = !showTechnologies || variant === 'compact' ? null : jsxRuntime.jsxs(material.Box, {
17196
+ sx: {
17197
+ mb: 3
17198
+ },
17199
+ children: [jsxRuntime.jsx(material.Typography, {
17200
+ variant: "h6",
17201
+ component: "h4",
17206
17202
  sx: {
17207
- mb: 3
17203
+ mb: 1.5,
17204
+ fontSize: '1rem',
17205
+ fontWeight: 600
17208
17206
  },
17209
- children: [jsxRuntime.jsx(material.Typography, {
17210
- variant: "h6",
17211
- component: "h4",
17212
- sx: {
17213
- mb: 1.5,
17214
- fontSize: '1rem',
17215
- fontWeight: 600
17216
- },
17217
- children: "Technologies:"
17218
- }), jsxRuntime.jsx(material.Box, {
17207
+ children: "Technologies:"
17208
+ }), jsxRuntime.jsx(material.Box, {
17209
+ sx: {
17210
+ display: 'flex',
17211
+ flexWrap: 'wrap',
17212
+ gap: 1
17213
+ },
17214
+ children: product.technologies.map((tech, index) => jsxRuntime.jsx(material.Chip, {
17215
+ label: tech,
17216
+ variant: "outlined",
17217
+ size: "small",
17219
17218
  sx: {
17220
- display: 'flex',
17221
- flexWrap: 'wrap',
17222
- gap: 1
17223
- },
17224
- children: product.technologies.map((tech, index) => jsxRuntime.jsx(material.Chip, {
17225
- label: tech,
17226
- variant: "outlined",
17227
- size: "small",
17228
- sx: {
17229
- fontSize: '0.8rem',
17230
- fontWeight: 500
17231
- }
17232
- }, index))
17233
- })]
17234
- });
17235
- };
17219
+ fontSize: '0.8rem',
17220
+ fontWeight: 500
17221
+ }
17222
+ }, index))
17223
+ })]
17224
+ });
17236
17225
  return jsxRuntime.jsxs(material.Box, {
17237
17226
  className: styleProps.className || "product-card",
17238
17227
  onClick: htmlProps.onClick || (variant === 'compact' ? handleProductClick : undefined),
@@ -17338,7 +17327,7 @@ function ProductCardView({
17338
17327
  },
17339
17328
  children: variant === 'detailed' ? product.description : product.shortDescription || product.description
17340
17329
  })]
17341
- }), jsxRuntime.jsx(FeaturesList, {}), jsxRuntime.jsx(TechnologiesSection, {}), jsxRuntime.jsx(material.Box, {
17330
+ }), featuresListElement, technologiesSectionElement, jsxRuntime.jsx(material.Box, {
17342
17331
  sx: {
17343
17332
  display: 'flex',
17344
17333
  gap: 1.5,
@@ -18199,7 +18188,6 @@ const ThemeProvider = ({
18199
18188
  }
18200
18189
  }
18201
18190
  });
18202
- // eslint-disable-next-line react-hooks/exhaustive-deps
18203
18191
  }, [actualThemeMode, paletteChangeCounter]); // paletteChangeCounter is needed to force theme rebuild // Re-create theme when palette changes
18204
18192
  // Theme preference management methods
18205
18193
  const setPreferredTheme = theme => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwickapps/react-framework",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "type": "module",
5
5
  "description": "Complete React framework with responsive navigation, flexible layouts, theming system, and reusable components for building modern applications.",
6
6
  "main": "dist/index.js",
@@ -61,85 +61,33 @@ function ContentView({
61
61
  }
62
62
  };
63
63
 
64
- // Content wrapper based on variant
65
- const ContentWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
66
- const paddingValue = getPadding();
67
- const mappedMaxWidth = mapToMUIBreakpoint(contentMaxWidth === 'false' ? false : contentMaxWidth as BreakpointValue);
68
- const commonSx: SxProps<Theme> = {
69
- textAlign: centered ? 'center' : 'left',
70
- maxWidth: mappedMaxWidth !== false
71
- ? theme.breakpoints.values[mappedMaxWidth]
72
- : '100%',
73
- width: '100%',
74
- ...(centered && contentMaxWidth && {
75
- mx: 'auto',
76
- }),
77
- p: paddingValue,
78
- ...styleProps.sx,
79
- };
80
-
81
- switch (variant) {
82
- case 'elevated':
83
- return (
84
- <Paper
85
- elevation={4}
86
- {...htmlProps}
87
- {...otherProps}
88
- sx={{
89
- ...commonSx,
90
- backgroundColor: theme.palette.background.paper,
91
- }}
92
- >
93
- {children}
94
- </Paper>
95
- );
96
- case 'outlined':
97
- return (
98
- <Paper
99
- variant="outlined"
100
- elevation={0}
101
- {...htmlProps}
102
- {...otherProps}
103
- sx={{
104
- ...commonSx,
105
- backgroundColor: 'var(--theme-surface)',
106
- borderColor: 'var(--theme-border-main)', // Use theme border color
107
- borderWidth: 1,
108
- borderStyle: 'solid',
109
- }}
110
- >
111
- {children}
112
- </Paper>
113
- );
114
- case 'filled':
115
- return (
116
- <Box
117
- {...htmlProps}
118
- {...otherProps}
119
- sx={{
120
- ...commonSx,
121
- backgroundColor: 'var(--theme-surface-variant)', // Use theme surface variant
122
- borderRadius: 1,
123
- }}
124
- >
125
- {children}
126
- </Box>
127
- );
128
- default:
129
- return (
130
- <Box
131
- {...htmlProps}
132
- {...otherProps}
133
- sx={commonSx}
134
- >
135
- {children}
136
- </Box>
137
- );
138
- }
64
+ // Compute stable wrapper element (avoid recreating component type each render which caused remount & focus loss)
65
+ const paddingValue = getPadding();
66
+ const mappedMaxWidth = mapToMUIBreakpoint(contentMaxWidth === 'false' ? false : contentMaxWidth as BreakpointValue);
67
+ const commonSx: SxProps<Theme> = {
68
+ textAlign: centered ? 'center' : 'left',
69
+ maxWidth: mappedMaxWidth !== false ? theme.breakpoints.values[mappedMaxWidth] : '100%',
70
+ width: '100%',
71
+ ...(centered && contentMaxWidth && { mx: 'auto' }),
72
+ p: paddingValue,
73
+ ...styleProps.sx,
139
74
  };
140
75
 
76
+ let Wrapper: React.ElementType = Box;
77
+ let wrapperProps: Record<string, any> = { ...htmlProps, ...otherProps, sx: commonSx };
78
+ if (variant === 'elevated') {
79
+ Wrapper = Paper;
80
+ wrapperProps = { ...wrapperProps, elevation: 4, sx: { ...commonSx, backgroundColor: theme.palette.background.paper } };
81
+ } else if (variant === 'outlined') {
82
+ Wrapper = Paper;
83
+ wrapperProps = { ...wrapperProps, variant: 'outlined', elevation: 0, sx: { ...commonSx, backgroundColor: 'var(--theme-surface)', borderColor: 'var(--theme-border-main)', borderWidth: 1, borderStyle: 'solid' } };
84
+ } else if (variant === 'filled') {
85
+ Wrapper = Box;
86
+ wrapperProps = { ...wrapperProps, sx: { ...commonSx, backgroundColor: 'var(--theme-surface-variant)', borderRadius: 1 } };
87
+ }
88
+
141
89
  return (
142
- <ContentWrapper>
90
+ <Wrapper {...wrapperProps}>
143
91
  <Stack spacing={2}>
144
92
  {/* Header */}
145
93
  {(title || subtitle) && (
@@ -210,7 +158,7 @@ function ContentView({
210
158
  </Stack>
211
159
  )}
212
160
  </Stack>
213
- </ContentWrapper>
161
+ </Wrapper>
214
162
  );
215
163
  }
216
164
 
@@ -141,26 +141,29 @@ function ProductCardView({
141
141
 
142
142
  const displayActions = actions || getDefaultActions();
143
143
 
144
- const FeaturesList = () => {
145
- const featuresToShow = variant === 'compact'
144
+ // INLINE WRAPPERS REFACTORED: Instead of inline component declarations (which cause remounts),
145
+ // compute JSX fragments via plain variables/functions and inject directly.
146
+
147
+ const featuresListElement = (() => {
148
+ const featuresToShow = variant === 'compact'
146
149
  ? (product.features || []).slice(0, maxFeaturesCompact)
147
150
  : (product.features || []);
148
151
 
149
152
  return (
150
153
  <Box sx={{ mb: variant === 'detailed' ? 2.5 : 2 }}>
151
- <Typography
152
- variant="h6"
154
+ <Typography
155
+ variant="h6"
153
156
  component="h4"
154
- sx={{
155
- mb: 1.5,
157
+ sx={{
158
+ mb: 1.5,
156
159
  fontSize: '1.1rem',
157
160
  fontWeight: 600
158
161
  }}
159
162
  >
160
163
  Key Features:
161
164
  </Typography>
162
- <Box component="ul" sx={{
163
- m: 0,
165
+ <Box component="ul" sx={{
166
+ m: 0,
164
167
  p: 0,
165
168
  listStyle: 'none'
166
169
  }}>
@@ -185,9 +188,9 @@ function ProductCardView({
185
188
  flexShrink: 0
186
189
  }}
187
190
  />
188
- <Typography
189
- variant="body2"
190
- sx={{
191
+ <Typography
192
+ variant="body2"
193
+ sx={{
191
194
  fontSize: '0.95rem',
192
195
  lineHeight: 1.4,
193
196
  opacity: 0.8
@@ -198,10 +201,10 @@ function ProductCardView({
198
201
  </Box>
199
202
  ))}
200
203
  {variant === 'compact' && (product.features || []).length > maxFeaturesCompact && (
201
- <Typography
202
- variant="body2"
204
+ <Typography
205
+ variant="body2"
203
206
  color="primary"
204
- sx={{
207
+ sx={{
205
208
  fontSize: '0.9rem',
206
209
  pl: 2.5,
207
210
  fontWeight: 500
@@ -213,41 +216,37 @@ function ProductCardView({
213
216
  </Box>
214
217
  </Box>
215
218
  );
216
- };
217
-
218
- const TechnologiesSection = () => {
219
- if (!showTechnologies || variant === 'compact') return null;
219
+ })();
220
220
 
221
- return (
222
- <Box sx={{ mb: 3 }}>
223
- <Typography
224
- variant="h6"
225
- component="h4"
226
- sx={{
227
- mb: 1.5,
228
- fontSize: '1rem',
229
- fontWeight: 600
230
- }}
231
- >
232
- Technologies:
233
- </Typography>
234
- <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
235
- {product.technologies.map((tech, index) => (
236
- <Chip
237
- key={index}
238
- label={tech}
239
- variant="outlined"
240
- size="small"
241
- sx={{
242
- fontSize: '0.8rem',
243
- fontWeight: 500
244
- }}
245
- />
246
- ))}
247
- </Box>
221
+ const technologiesSectionElement = (!showTechnologies || variant === 'compact') ? null : (
222
+ <Box sx={{ mb: 3 }}>
223
+ <Typography
224
+ variant="h6"
225
+ component="h4"
226
+ sx={{
227
+ mb: 1.5,
228
+ fontSize: '1rem',
229
+ fontWeight: 600
230
+ }}
231
+ >
232
+ Technologies:
233
+ </Typography>
234
+ <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
235
+ {product.technologies.map((tech, index) => (
236
+ <Chip
237
+ key={index}
238
+ label={tech}
239
+ variant="outlined"
240
+ size="small"
241
+ sx={{
242
+ fontSize: '0.8rem',
243
+ fontWeight: 500
244
+ }}
245
+ />
246
+ ))}
248
247
  </Box>
249
- );
250
- };
248
+ </Box>
249
+ );
251
250
 
252
251
  return (
253
252
  <Box
@@ -364,11 +363,11 @@ function ProductCardView({
364
363
  </Typography>
365
364
  </Box>
366
365
 
367
- {/* Features */}
368
- <FeaturesList />
366
+ {/* Features */}
367
+ {featuresListElement}
369
368
 
370
- {/* Technologies */}
371
- <TechnologiesSection />
369
+ {/* Technologies */}
370
+ {technologiesSectionElement}
372
371
 
373
372
  {/* Action Buttons */}
374
373
  <Box sx={{
@@ -302,8 +302,7 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({
302
302
  },
303
303
  },
304
304
  },
305
- });
306
- // eslint-disable-next-line react-hooks/exhaustive-deps
305
+ });
307
306
  }, [actualThemeMode, paletteChangeCounter]); // paletteChangeCounter is needed to force theme rebuild // Re-create theme when palette changes
308
307
 
309
308
  // Theme preference management methods