payaza-storefront-layouts 1.0.11 → 1.0.12

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ShadowDOMWrapper.d.ts","sourceRoot":"","sources":["../../src/preview/ShadowDOMWrapper.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CAyO/G"}
1
+ {"version":3,"file":"ShadowDOMWrapper.d.ts","sourceRoot":"","sources":["../../src/preview/ShadowDOMWrapper.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAqB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrD,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CA2M/G"}
@@ -18,6 +18,8 @@ export function ShadowDOMWrapper({ children, className, onReady, onLinkClick, st
18
18
  const reactRootRef = useRef(null);
19
19
  const containerRef = useRef(null);
20
20
  const childrenRef = useRef(children);
21
+ const isUnmountingRef = useRef(false);
22
+ const isMountedRef = useRef(false);
21
23
  // Keep children ref updated
22
24
  useEffect(() => {
23
25
  childrenRef.current = children;
@@ -25,13 +27,16 @@ export function ShadowDOMWrapper({ children, className, onReady, onLinkClick, st
25
27
  useEffect(() => {
26
28
  if (!shadowHostRef.current)
27
29
  return;
30
+ // Reset unmounting flag when mounting
31
+ isUnmountingRef.current = false;
32
+ isMountedRef.current = true;
28
33
  // Check if shadow root already exists
29
34
  const existingShadowRoot = shadowHostRef.current.shadowRoot;
30
35
  if (existingShadowRoot) {
31
36
  shadowRootRef.current = existingShadowRoot;
32
37
  // Update content if shadow root already exists
33
38
  const container = existingShadowRoot.getElementById('shadow-preview-container');
34
- if (container && reactRootRef.current) {
39
+ if (container && reactRootRef.current && !isUnmountingRef.current) {
35
40
  reactRootRef.current.render(childrenRef.current);
36
41
  }
37
42
  onReady?.();
@@ -68,58 +73,9 @@ export function ShadowDOMWrapper({ children, className, onReady, onLinkClick, st
68
73
  // Create React root and render children
69
74
  const root = createRoot(container);
70
75
  reactRootRef.current = root;
71
- root.render(childrenRef.current);
72
- // Set up link click interception inside Shadow DOM
73
- if (onLinkClick) {
74
- const handleClick = (e) => {
75
- const target = e.target;
76
- const link = target.closest('a[href]');
77
- if (!link || !link.href)
78
- return;
79
- try {
80
- const url = new URL(link.href, window.location.origin);
81
- const pathname = url.pathname;
82
- // Only intercept internal links
83
- if (url.origin !== window.location.origin) {
84
- return; // External link, allow default behavior
85
- }
86
- // Extract route from href (remove store slug prefix if present)
87
- let route = pathname;
88
- // Check if pathname starts with store slug (e.g., /modern-eats/contact)
89
- if (storeSlug && pathname.startsWith(`/${storeSlug}`)) {
90
- // Remove the store slug prefix: /modern-eats/contact -> /contact
91
- route = pathname.slice(storeSlug.length + 1);
92
- // If route is empty after removing slug, it's the homepage
93
- if (!route || route === '') {
94
- route = '/';
95
- }
96
- }
97
- else if (pathname === '/' || pathname === '') {
98
- // Homepage link
99
- route = '/';
100
- }
101
- // Normalize route (ensure it starts with /)
102
- if (route && !route.startsWith('/')) {
103
- route = `/${route}`;
104
- }
105
- // Ensure route is not empty
106
- if (!route) {
107
- route = '/';
108
- }
109
- // Note: We don't preserve query string and hash here because
110
- // the route is stored as a query param. Query strings and hash
111
- // should be handled separately if needed.
112
- // Prevent default navigation and call handler
113
- e.preventDefault();
114
- e.stopPropagation();
115
- onLinkClick(route);
116
- }
117
- catch (err) {
118
- // If URL parsing fails, allow default behavior
119
- console.warn('[ShadowDOMWrapper] Failed to parse link URL:', err, link.href);
120
- }
121
- };
122
- // Handler will be set up in separate useEffect to handle prop changes
76
+ // Only render if not unmounting
77
+ if (!isUnmountingRef.current) {
78
+ root.render(childrenRef.current);
123
79
  }
124
80
  onReady?.();
125
81
  }
@@ -127,22 +83,42 @@ export function ShadowDOMWrapper({ children, className, onReady, onLinkClick, st
127
83
  console.error('[ShadowDOMWrapper] Failed to create shadow root:', err);
128
84
  }
129
85
  return () => {
130
- // Cleanup
131
- if (reactRootRef.current) {
132
- try {
133
- reactRootRef.current.unmount();
134
- }
135
- catch (e) {
136
- console.warn('[ShadowDOMWrapper] Cleanup warning:', e);
137
- }
138
- reactRootRef.current = null;
86
+ // Mark as unmounting to prevent concurrent renders
87
+ isUnmountingRef.current = true;
88
+ isMountedRef.current = false;
89
+ // Defer unmounting to avoid race condition with React's rendering cycle
90
+ // Use setTimeout to ensure React finishes its current render before unmounting
91
+ const rootToUnmount = reactRootRef.current;
92
+ reactRootRef.current = null;
93
+ if (rootToUnmount) {
94
+ setTimeout(() => {
95
+ try {
96
+ rootToUnmount.unmount();
97
+ }
98
+ catch (e) {
99
+ // Ignore errors during unmount - root may already be unmounted
100
+ console.warn('[ShadowDOMWrapper] Unmount warning (safe to ignore):', e);
101
+ }
102
+ }, 0);
139
103
  }
140
104
  };
141
105
  }, [onReady]);
142
106
  // Update content when children change
143
107
  useEffect(() => {
108
+ // Guard: Don't render if unmounting or not mounted
109
+ if (isUnmountingRef.current || !isMountedRef.current) {
110
+ return;
111
+ }
144
112
  if (reactRootRef.current && containerRef.current) {
145
- reactRootRef.current.render(childrenRef.current);
113
+ try {
114
+ reactRootRef.current.render(childrenRef.current);
115
+ }
116
+ catch (e) {
117
+ // Ignore render errors if we're unmounting
118
+ if (!isUnmountingRef.current) {
119
+ console.warn('[ShadowDOMWrapper] Render error:', e);
120
+ }
121
+ }
146
122
  }
147
123
  }, [children]);
148
124
  // Set up link click handler when shadow root and props are ready
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payaza-storefront-layouts",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Shared layout components for StoreFront applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",