@geotab/zenith 3.6.0-ssr.beta.1 → 3.6.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.
@@ -20,7 +20,7 @@ import { SummaryTileDisplayName } from "../summaryTile/summaryTile";
20
20
  import { FullScreenButton } from "../dataGrid/entitiesListActions/actions/fullscreenButton";
21
21
  import { useMobile } from "../commonHelpers/hooks/useMobile";
22
22
  import { PageHeaderDisplayName } from "../pageHeader/pageHeader";
23
- // ...existing code...
23
+ import { useClientReady } from "../commonHelpers/hooks/useClientReady";
24
24
  import { zen } from "../utils/zen";
25
25
  export const getContentPaddings = (isMobile, startWithToolbar) => {
26
26
  const paddingsCount = startWithToolbar ? 1 : 2;
@@ -36,7 +36,7 @@ const LayoutInner = ({ id, className, pageScrolling = true, children }) => {
36
36
  const [minHeight, setMinHeight] = useState(zen.innerHeight || 0);
37
37
  const [startWithToolbar, setStartWithToolbar] = useState(false);
38
38
  const isFullScreen = useCallback(() => fullScreenElement !== undefined, [fullScreenElement]);
39
- // ...existing code...
39
+ const isClientReady = useClientReady();
40
40
  const [header, banners, summaryTileBar, content] = useMemo(() => {
41
41
  const h = [];
42
42
  const b = [];
@@ -149,16 +149,22 @@ const LayoutInner = ({ id, className, pageScrolling = true, children }) => {
149
149
  useResize(resizeCallBack, true);
150
150
  const isFullScreenCurrent = isFullScreen();
151
151
  useEffect(() => {
152
+ if (!isClientReady) {
153
+ return;
154
+ }
152
155
  calculateLayoutParams(layoutRef.current, middleRef.current);
153
- }, [calculateLayoutParams, layoutRef, middleRef]);
156
+ }, [calculateLayoutParams, isClientReady, layoutRef, middleRef]);
154
157
  useEffect(() => {
155
158
  var _a;
159
+ if (!isClientReady) {
160
+ return undefined;
161
+ }
156
162
  const timeoutId = (_a = zen.setTimeout) === null || _a === void 0 ? void 0 : _a.call(zen, () => {
157
163
  const isStartWithToolbar = checkIsStartWithToolbar(isFullScreenCurrent);
158
164
  setStartWithToolbar(isStartWithToolbar);
159
165
  }, 0);
160
166
  return () => { var _a; return (_a = zen.clearTimeout) === null || _a === void 0 ? void 0 : _a.call(zen, timeoutId); };
161
- }, [isFullScreenCurrent, checkIsStartWithToolbar]);
167
+ }, [isFullScreenCurrent, checkIsStartWithToolbar, isClientReady]);
162
168
  const memoizedHeader = useMemo(() => header, [header]);
163
169
  const memoizedBanners = useMemo(() => banners, [banners]);
164
170
  const memoizedContent = useMemo(() => content, [content]);
@@ -182,6 +188,9 @@ const LayoutInner = ({ id, className, pageScrolling = true, children }) => {
182
188
  }, [isFullScreenCurrent, minHeight, pageScrolling]);
183
189
  useEffect(() => {
184
190
  var _a, _b;
191
+ if (!isClientReady) {
192
+ return;
193
+ }
185
194
  if (isFullScreenCurrent) {
186
195
  const fullscreenEl = (_a = layoutRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(".zen-layout__full-screen-element--visible");
187
196
  if (fullscreenEl) {
@@ -200,7 +209,10 @@ const LayoutInner = ({ id, className, pageScrolling = true, children }) => {
200
209
  });
201
210
  }
202
211
  }
203
- }, [isFullScreenCurrent, layoutRef]);
212
+ }, [isClientReady, isFullScreenCurrent, layoutRef]);
213
+ if (!isClientReady) {
214
+ return null;
215
+ }
204
216
  return (_jsx("section", Object.assign({ id: id || layoutId, ref: layoutRef, className: classNames(["zen-layout", !pageScrolling ? "zen-layout--no-scroll" : "", className || ""]) }, heightStyle, { children: _jsx(LayoutFullScreenProvider, { isFullScreen: isFullScreen, setFullScreenElement: handleSetFullScreenElement, fullScreenElement: fullScreenElement, children: _jsxs(LayoutSizeProvider, { contentHeight: memoizedContentHeight, outerContentHeight: memoizedOuterContentHeight, isSetHeight: !pageScrolling, children: [isFullScreenCurrent ? null : memoizedHeader, isFullScreenCurrent ? null : memoizedBanners, isFullScreenCurrent ? null : memoizedSummaryTileBar, memoizedContent] }) }) })));
205
217
  };
206
218
  export const Layout = memo(LayoutInner);
@@ -16,7 +16,7 @@ import { useDriveClassName } from "../utils/theme/useDriveClassName";
16
16
  import { useLanguage } from "../utils/localization/useLanguage";
17
17
  import { useUniqueId } from "../commonHelpers/useUniqueId";
18
18
  import { useFadeComponent } from "../utils/useFadeComponent";
19
- // ...existing code...
19
+ import { useClientReady } from "../commonHelpers/hooks/useClientReady";
20
20
  import { zen } from "../utils/zen";
21
21
  // eslint-disable-next-line @typescript-eslint/naming-convention
22
22
  injectString("cs", "Back", "Zp\u011Bt");
@@ -132,12 +132,15 @@ export const MobileSheet = ({
132
132
  return;
133
133
  }
134
134
  }, [onHidePanel, dataShieldId]);
135
- // ...existing code...
135
+ const isClientReady = useClientReady();
136
136
  useEffect(() => {
137
137
  setIsOpen(isOpen);
138
138
  }, [isOpen, setIsOpen]);
139
139
  useEffect(() => {
140
140
  var _a, _b, _c, _d;
141
+ if (!isClientReady) {
142
+ return;
143
+ }
141
144
  if (useTrapFocusWithTrigger) {
142
145
  return;
143
146
  }
@@ -145,7 +148,7 @@ export const MobileSheet = ({
145
148
  return;
146
149
  }
147
150
  showContent && !preventFirstFocus && ((_d = (_c = ref.current) === null || _c === void 0 ? void 0 : _c.querySelector(FOCUSABLE_SELECTOR)) === null || _d === void 0 ? void 0 : _d.focus());
148
- }, [content, showContent, readyForFocus, useTrapFocusWithTrigger, preventFirstFocus]);
151
+ }, [content, showContent, readyForFocus, useTrapFocusWithTrigger, preventFirstFocus, isClientReady]);
149
152
  useEffect(() => {
150
153
  var _a;
151
154
  if (triggerRef && !renderComponent && !readyForFocus && ((_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.getAttribute("aria-expanded")) === "true") {
@@ -153,6 +156,9 @@ export const MobileSheet = ({
153
156
  triggerRef.current.focus();
154
157
  }
155
158
  }, [triggerRef, renderComponent, readyForFocus]);
159
+ if (!isClientReady) {
160
+ return null;
161
+ }
156
162
  if (!renderComponent && !isOpen) {
157
163
  return null;
158
164
  }
@@ -19,6 +19,7 @@ import { isFocusable } from "../dialog/dialogHelpers";
19
19
  import { themeContext } from "../utils/theme/themeContext";
20
20
  import { getPredefinedFocusableItem } from "./modalHelpers";
21
21
  import { TextIconButton } from "../textIconButton/textIconButton";
22
+ import { useClientReady } from "../commonHelpers/hooks/useClientReady";
22
23
  import { zen } from "../utils/zen";
23
24
  import { createPortal } from "react-dom";
24
25
  injectString("cs", "Close", "Zav\u0159\xEDt");
@@ -61,26 +62,34 @@ export const Modal = ({
61
62
  }) => {
62
63
  var _a, _b, _c;
63
64
  const lastActiveOutsideElement = useRef((_a = zen.document) === null || _a === void 0 ? void 0 : _a.activeElement);
64
- // eslint-disable-next-line
65
- const modalRoot = ((_b = zen === null || zen === void 0 ? void 0 : zen.document) === null || _b === void 0 ? void 0 : _b.fullscreenElement) || ((_c = zen === null || zen === void 0 ? void 0 : zen.document) === null || _c === void 0 ? void 0 : _c.body);
66
65
  const [top, setTop] = useState(`${zen.pageYOffset || 0}px`);
67
66
  const {
68
67
  dark
69
68
  } = useContext(themeContext);
69
+ const isClientReady = useClientReady();
70
70
  const onOutSideClick = useCallback(() => {
71
+ if (!isClientReady) {
72
+ return;
73
+ }
71
74
  if (!isOpen || !closeOnClickOutside || !onClose) {
72
75
  return;
73
76
  }
74
77
  onClose();
75
- }, [isOpen, closeOnClickOutside, onClose]);
78
+ }, [isClientReady, isOpen, closeOnClickOutside, onClose]);
76
79
  useEffect(() => {
80
+ if (!isClientReady) {
81
+ return undefined;
82
+ }
77
83
  const copyActive = lastActiveOutsideElement.current;
78
84
  return () => {
79
85
  isFocusable(copyActive) && copyActive.focus();
80
86
  };
81
- }, []);
87
+ }, [isClientReady]);
82
88
  useEffect(() => {
83
89
  var _a, _b;
90
+ if (!isClientReady) {
91
+ return undefined;
92
+ }
84
93
  (_a = zen.document) === null || _a === void 0 ? void 0 : _a.body.classList.remove("zen-modal-content--hidden-overflow");
85
94
  if (isOpen) {
86
95
  (_b = zen.document) === null || _b === void 0 ? void 0 : _b.body.classList.add("zen-modal-content--hidden-overflow");
@@ -89,9 +98,12 @@ export const Modal = ({
89
98
  var _a;
90
99
  (_a = zen.document) === null || _a === void 0 ? void 0 : _a.body.classList.remove("zen-modal-content--hidden-overflow");
91
100
  };
92
- }, [isOpen]);
101
+ }, [isClientReady, isOpen]);
93
102
  useEffect(() => {
94
103
  var _a, _b;
104
+ if (!isClientReady) {
105
+ return undefined;
106
+ }
95
107
  const changeTopSpace = () => {
96
108
  setTop(`${zen.pageYOffset || 0}px`);
97
109
  };
@@ -102,7 +114,7 @@ export const Modal = ({
102
114
  (_a = zen.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(zen, "resize", changeTopSpace);
103
115
  (_b = zen.removeEventListener) === null || _b === void 0 ? void 0 : _b.call(zen, "scroll", changeTopSpace);
104
116
  };
105
- }, []);
117
+ }, [isClientReady]);
106
118
  const labeledId = useId();
107
119
  const darkClass = dark ? "zen-dark" : "";
108
120
  const modalContent = useMemo(() => {
@@ -137,10 +149,11 @@ export const Modal = ({
137
149
  onClick: onOutSideClick
138
150
  })]
139
151
  }), [darkClass, modalContainerClassName, labeledId, top, onClose, maxWidth, title, className, type, focus, modalContent, onOutSideClick]);
140
- if (!isOpen) {
152
+ if (!isOpen || !isClientReady) {
141
153
  return null;
142
154
  }
143
- return createPortal(dialogHTML(labeledId), modalRoot, "ModalPortal" + labeledId);
155
+ const modalRoot = ((_b = zen.document) === null || _b === void 0 ? void 0 : _b.fullscreenElement) || ((_c = zen.document) === null || _c === void 0 ? void 0 : _c.body);
156
+ return createPortal(dialogHTML(labeledId), modalRoot);
144
157
  };
145
158
  const dummyOnClose = () => {};
146
159
  export const DialogContentNew = ({
@@ -162,10 +175,10 @@ export const DialogContentNew = ({
162
175
  const lastActiveOutsideElement = useRef((_a = zen.document) === null || _a === void 0 ? void 0 : _a.activeElement);
163
176
  const iconDriveClassName = useDriveClassName("icon");
164
177
  const iconsByType = useMemo(() => ({
165
- "error": IconException,
166
- "success": IconCheckRadio,
167
- "warning": IconWarning,
168
- "info": IconInfoCircle
178
+ error: IconException,
179
+ success: IconCheckRadio,
180
+ warning: IconWarning,
181
+ info: IconInfoCircle
169
182
  }), []);
170
183
  const subscriptionCondition = useCallback(trigger => trigger.current !== null, []);
171
184
  useTrapFocus(contentRef, undefined, contentRef, subscriptionCondition);
@@ -8,10 +8,8 @@ import { PageToolbarCollapsedItemsControl } from "../header/components/collapsed
8
8
  import { PageHeaderButtonDisplayName } from "./pageHeaderButton";
9
9
  import { PageHeaderMenuDisplayName } from "./pageHeaderMenu";
10
10
  import { getItemsFromFragments } from "./pageHeaderHelpers";
11
- import { createPortal } from "react-dom";
12
- import { zen } from "../utils/zen";
11
+ import { usePortal } from "../commonHelpers/hooks/usePortal";
13
12
  export const PageHeaderActions = ({ className = "", children }) => {
14
- var _a;
15
13
  const isMobile = useMobile();
16
14
  const childrenArray = useMemo(() => getItemsFromFragments(
17
15
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -28,9 +26,10 @@ export const PageHeaderActions = ({ className = "", children }) => {
28
26
  }), [childrenArray]);
29
27
  const moreButton = useMemo(() => (_jsx(PageToolbarCollapsedItemsControl, { className: "zen-page-header-component__actions-more", children: nonFittingElements }, "page-header-more-actions-key")), [nonFittingElements]);
30
28
  const actionsContent = childrenArray.length ? (_jsxs("div", { className: classNames(["zen-page-header-component__actions", isMobile ? "zen-page-header-component__actions--mobile-portal" : "", className]), children: [nonFittingElements.length > 0 ? moreButton : null, fittingElements] })) : null;
29
+ const portal = usePortal(actionsContent);
31
30
  // For mobile, render actions in a portal at the bottom center of viewport
32
31
  if (isMobile && actionsContent) {
33
- return createPortal(actionsContent, (_a = zen.document) === null || _a === void 0 ? void 0 : _a.body);
32
+ return portal;
34
33
  }
35
34
  return actionsContent;
36
35
  };
@@ -8,13 +8,14 @@ import { useTrapFocus } from "../commonHelpers/hooks/useTrapFocus";
8
8
  import { isChildOf } from "../utils/isChildOf";
9
9
  import { getParentByClass } from "../utils/getParentByClass";
10
10
  import { SidePanelCell } from "./sidePanelCell/sidePanelCell";
11
- import { createPortal } from "react-dom";
12
11
  import { useDriveClassName } from "../utils/theme/useDriveClassName";
13
12
  import { themeContext } from "../utils/theme/themeContext";
14
13
  import { useFadeComponent } from "../utils/useFadeComponent";
15
14
  import { useMobile } from "../commonHelpers/hooks/useMobile";
16
15
  import { isAlertTarget } from "../alert/utils/isAlertTarget";
17
16
  import { isToastTarget } from "../toast/utils/isToastTarget";
17
+ import { usePortal } from "../commonHelpers/hooks/usePortal";
18
+ import { useClientReady } from "../commonHelpers/hooks/useClientReady";
18
19
  import { zen } from "../utils/zen";
19
20
  /* eslint-enable @typescript-eslint/naming-convention */
20
21
  export const isChildPopup = (target, stopElement) => {
@@ -60,15 +61,16 @@ export const SidePanel = ({ label, className, id, isOpen, onHidePanel, children,
60
61
  const autoId = useId();
61
62
  const popupId = id || autoId;
62
63
  const prevReadyForFocus = useRef(false);
64
+ const isClientReady = useClientReady();
63
65
  useEffect(() => {
64
- if (triggerRef && triggerRef.current) {
66
+ if (triggerRef && triggerRef.current && isClientReady) {
65
67
  const triggerRole = triggerRef.current.dataset.role;
66
68
  triggerRef.current.setAttribute("aria-haspopup", triggerRole ? triggerRole : "true");
67
69
  triggerRef.current.setAttribute("aria-expanded", renderComponent ? "true" : "false");
68
70
  triggerRef.current.setAttribute("aria-controls", popupId);
69
71
  triggerRef.current.setAttribute("data-popup-id", popupId);
70
72
  }
71
- }, [triggerRef, renderComponent, popupId]);
73
+ }, [triggerRef, renderComponent, popupId, isClientReady]);
72
74
  function handleEscape() {
73
75
  onHidePanel(SidePanelCloseReason.Escape);
74
76
  if (!triggerRef || !triggerRef.current) {
@@ -81,10 +83,13 @@ export const SidePanel = ({ label, className, id, isOpen, onHidePanel, children,
81
83
  const focusable = Array.from(triggerRef.current.querySelectorAll(FOCUSABLE_SELECTOR));
82
84
  (focusable[0] || triggerRef.current).focus();
83
85
  }
84
- useTrapFocus(sidePanelRef, useTrapFocusWithTrigger ? triggerRef : undefined, showContent);
85
- useEscape(sidePanelRef, handleEscape, renderComponent);
86
+ useTrapFocus(sidePanelRef, useTrapFocusWithTrigger ? triggerRef : undefined, showContent && isClientReady);
87
+ useEscape(sidePanelRef, handleEscape, renderComponent && isClientReady);
86
88
  useEffect(() => {
87
89
  var _a, _b, _c;
90
+ if (!isClientReady) {
91
+ return undefined;
92
+ }
88
93
  // eslint-disable-next-line complexity
89
94
  const closeOnClickOutside = (e) => {
90
95
  var _a, _b, _c, _d, _e, _f;
@@ -112,7 +117,7 @@ export const SidePanel = ({ label, className, id, isOpen, onHidePanel, children,
112
117
  || isOpenPeriodPicker && isChildOf(e.target, isOpenPeriodPicker)
113
118
  || isOpenPeriodPicker && isOpenTimePicker && isChildOf(e.target, isOpenTimePicker)
114
119
  || isOpenPeriodPicker && isOpenYearPicker && isChildOf(e.target, isOpenYearPicker)
115
- || openedCustomComponents.length && Array.from(openedCustomComponents).some(el => isChildOf(e.target, el));
120
+ || (openedCustomComponents === null || openedCustomComponents === void 0 ? void 0 : openedCustomComponents.length) && Array.from(openedCustomComponents).some(el => isChildOf(e.target, el));
116
121
  const isClickInPopup = !isClickInChildPopup
117
122
  ? false
118
123
  : isClickInChildPopup;
@@ -142,7 +147,7 @@ export const SidePanel = ({ label, className, id, isOpen, onHidePanel, children,
142
147
  prevReadyForFocus.current = false;
143
148
  }
144
149
  return () => { var _a; return (_a = zen.document) === null || _a === void 0 ? void 0 : _a.body.removeEventListener("click", closeOnClickOutside, true); };
145
- }, [renderComponent, readyForFocus, onHidePanel, triggerRef, useTrapFocusWithTrigger, preventFirstFocus]);
150
+ }, [renderComponent, readyForFocus, onHidePanel, triggerRef, useTrapFocusWithTrigger, preventFirstFocus, isClientReady]);
146
151
  useEffect(() => {
147
152
  setIsOpen(isOpen);
148
153
  }, [isOpen, setIsOpen]);
@@ -156,16 +161,21 @@ export const SidePanel = ({ label, className, id, isOpen, onHidePanel, children,
156
161
  }
157
162
  return {};
158
163
  }, [width, isMobile, panelPosition]);
159
- if (!renderComponent && !isOpen) {
160
- return null;
161
- }
162
- return createPortal(_jsx("div", Object.assign({ ref: sidePanelRef, id: popupId }, memoizedStyles, { role: "dialog", "aria-label": label, onTransitionEnd: handleTransitionEnd, className: classNames([
164
+ const portalContainer = ((_a = zen.document) === null || _a === void 0 ? void 0 : _a.fullscreenElement) || ((_b = zen.document) === null || _b === void 0 ? void 0 : _b.body);
165
+ const portal = usePortal(_jsx("div", Object.assign({ ref: sidePanelRef, id: popupId }, memoizedStyles, { role: "dialog", "aria-label": label, onTransitionEnd: handleTransitionEnd, className: classNames([
163
166
  "zen-side-panel",
164
167
  driveClassName || "",
165
168
  dark ? "zen-dark" : "",
166
169
  showContent ? "zen-side-panel--shown" : "",
167
170
  positioningClass,
168
171
  className !== null && className !== void 0 ? className : ""
169
- ]), children: memoizedChildForRender })), ((_a = zen.document) === null || _a === void 0 ? void 0 : _a.fullscreenElement) || ((_b = zen.document) === null || _b === void 0 ? void 0 : _b.body));
172
+ ]), children: memoizedChildForRender })), portalContainer);
173
+ if (!renderComponent && !isOpen) {
174
+ return null;
175
+ }
176
+ if (!isClientReady) {
177
+ return undefined;
178
+ }
179
+ return portal;
170
180
  };
171
181
  SidePanel.Cell = SidePanelCell;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geotab/zenith",
3
- "version": "3.6.0-ssr.beta.1",
3
+ "version": "3.6.0",
4
4
  "description": "Zenith components library on React",
5
5
  "main": "dist/index.js",
6
6
  "types": "esm/index.d.ts",
@@ -98,6 +98,9 @@
98
98
  "react": "^19.2.0",
99
99
  "react-dom": "^19.2.0"
100
100
  },
101
+ "overrides": {
102
+ "serialize-javascript": "^7.0.3"
103
+ },
101
104
  "files": [
102
105
  "./dist/",
103
106
  "./esm"