@blerp/design 1.4.1 → 1.4.3

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.
@@ -22,10 +22,13 @@ const _excluded = ["options", "value", "defaultValue", "onChange", "onInputChang
22
22
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
23
23
 
24
24
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
25
+
26
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined';
25
27
  /**
26
28
  * Autocomplete - combo box with filtering
27
29
  */
28
30
 
31
+
29
32
  const AutocompleteBase = /*#__PURE__*/React__default["default"].forwardRef((_ref, ref) => {
30
33
  let {
31
34
  options = [],
@@ -174,6 +177,8 @@ const AutocompleteBase = /*#__PURE__*/React__default["default"].forwardRef((_ref
174
177
 
175
178
 
176
179
  React.useEffect(() => {
180
+ if (!canUseDOM()) return;
181
+
177
182
  const handleClickOutside = event => {
178
183
  if (inputRef.current && !inputRef.current.contains(event.target) && listboxRef.current && !listboxRef.current.contains(event.target)) {
179
184
  handleClose();
@@ -244,6 +244,11 @@ const BaseModal = /*#__PURE__*/React__default["default"].forwardRef((_ref2, ref)
244
244
 
245
245
  if (disablePortal) {
246
246
  return modalContent;
247
+ } // SSR check - don't use createPortal on the server
248
+
249
+
250
+ if (typeof window === 'undefined') {
251
+ return null;
247
252
  }
248
253
 
249
254
  return /*#__PURE__*/ReactDOM.createPortal(modalContent, container || document.body);
@@ -50,6 +50,8 @@ const resolveStyles = (styleObj, colors) => {
50
50
 
51
51
  return resolved;
52
52
  };
53
+
54
+ const canUseDOM = () => typeof window !== "undefined" && typeof document !== "undefined";
53
55
  /**
54
56
  * Input/TextField - Form input component (replaces MUI TextField)
55
57
  * Supports variants: outlined, filled, standard
@@ -340,7 +342,7 @@ const BaseInput = /*#__PURE__*/React__default["default"].forwardRef((_ref, ref)
340
342
  const handleClick = e => {
341
343
  onClick === null || onClick === void 0 ? void 0 : onClick(e);
342
344
 
343
- if (!disabled) {
345
+ if (!disabled && canUseDOM()) {
344
346
  const input = document.getElementById(id);
345
347
  input === null || input === void 0 ? void 0 : input.focus();
346
348
  }
@@ -738,6 +738,7 @@ const AccordionSummary = /*#__PURE__*/React__default["default"].forwardRef((_ref
738
738
  style: iconStyle
739
739
  }, expandIcon));
740
740
  });
741
+ AccordionSummary.displayName = "AccordionSummary";
741
742
  /**
742
743
  * AccordionDetails - Content area
743
744
  */
@@ -29,6 +29,11 @@ const _excluded = ["orientation", "variant", "flexItem", "light", "textAlign", "
29
29
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
30
30
 
31
31
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
32
+
33
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined'; // Use useLayoutEffect only on client, useEffect on server
34
+
35
+
36
+ const useIsomorphicLayoutEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect;
32
37
  /**
33
38
  * Divider - Visual separator (replaces MUI Divider)
34
39
  */
@@ -471,13 +476,18 @@ const BaseTooltip = /*#__PURE__*/React__default["default"].forwardRef((_ref5, re
471
476
  top: 0,
472
477
  left: 0
473
478
  });
479
+ const [mounted, setMounted] = React.useState(false);
474
480
  const timeoutRef = React.useRef(null);
475
481
  const childRef = React.useRef(null);
476
482
  const tooltipRef = React.useRef(null);
477
- const open = controlledOpen !== undefined ? controlledOpen : isOpen; // Calculate position based on child element and placement
483
+ const open = controlledOpen !== undefined ? controlledOpen : isOpen; // SSR safety - only mount portal after hydration
484
+
485
+ React.useEffect(() => {
486
+ setMounted(true);
487
+ }, []); // Calculate position based on child element and placement
478
488
 
479
489
  const updatePosition = React.useCallback(() => {
480
- if (!childRef.current) return;
490
+ if (!canUseDOM() || !childRef.current) return;
481
491
  const childRect = childRef.current.getBoundingClientRect();
482
492
  const tooltipEl = tooltipRef.current;
483
493
  const tooltipRect = tooltipEl ? tooltipEl.getBoundingClientRect() : {
@@ -577,17 +587,16 @@ const BaseTooltip = /*#__PURE__*/React__default["default"].forwardRef((_ref5, re
577
587
  });
578
588
  }, [placement]); // Update position when open changes or on scroll/resize
579
589
 
580
- React.useLayoutEffect(() => {
581
- if (open) {
582
- updatePosition(); // Update position on scroll and resize
590
+ useIsomorphicLayoutEffect(() => {
591
+ if (!canUseDOM() || !open) return;
592
+ updatePosition(); // Update position on scroll and resize
583
593
 
584
- window.addEventListener('scroll', updatePosition, true);
585
- window.addEventListener('resize', updatePosition);
586
- return () => {
587
- window.removeEventListener('scroll', updatePosition, true);
588
- window.removeEventListener('resize', updatePosition);
589
- };
590
- }
594
+ window.addEventListener('scroll', updatePosition, true);
595
+ window.addEventListener('resize', updatePosition);
596
+ return () => {
597
+ window.removeEventListener('scroll', updatePosition, true);
598
+ window.removeEventListener('resize', updatePosition);
599
+ };
591
600
  }, [open, updatePosition]); // Re-calculate after tooltip renders (to get accurate dimensions)
592
601
 
593
602
  React.useEffect(() => {
@@ -688,9 +697,10 @@ const BaseTooltip = /*#__PURE__*/React__default["default"].forwardRef((_ref5, re
688
697
  }, (_componentsProps$tool3 = componentsProps.tooltip) === null || _componentsProps$tool3 === void 0 ? void 0 : _componentsProps$tool3.sx), PopperProps.sx), {}, {
689
698
  // Force background at the end to ensure it's never transparent
690
699
  backgroundColor
691
- });
700
+ }); // Don't render portal during SSR or before mount
701
+
692
702
 
693
- const tooltipContent = open && title ? /*#__PURE__*/React__default["default"].createElement("span", {
703
+ const tooltipContent = open && title && mounted && canUseDOM() ? /*#__PURE__*/React__default["default"].createElement("span", {
694
704
  ref: tooltipRef,
695
705
  role: "tooltip",
696
706
  style: tooltipStyle
@@ -32,6 +32,11 @@ const _excluded = ["open", "onClose", "anchorEl", "anchorOrigin", "transformOrig
32
32
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
33
33
 
34
34
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
35
+
36
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined'; // Use useLayoutEffect only on client, useEffect on server
37
+
38
+
39
+ const useIsomorphicLayoutEffect = canUseDOM() ? React.useLayoutEffect : React.useEffect;
35
40
  /**
36
41
  * Context to detect if we're inside a Popover
37
42
  */
@@ -106,6 +111,7 @@ function resolveStyleColors(style, colors) {
106
111
 
107
112
 
108
113
  function isClickInsideAnyOverlay(target, excludeElement) {
114
+ if (!canUseDOM()) return false;
109
115
  let element = target;
110
116
 
111
117
  while (element && element !== document.body) {
@@ -180,10 +186,15 @@ const BaseMenu = /*#__PURE__*/React__default["default"].forwardRef((_ref2, ref)
180
186
  top: 0,
181
187
  left: 0
182
188
  });
189
+ const [mounted, setMounted] = React.useState(false);
183
190
  const {
184
191
  registerOverlay,
185
192
  isClickInsideOverlay
186
- } = useOverlayStack(); // Combine refs
193
+ } = useOverlayStack(); // SSR safety
194
+
195
+ React.useEffect(() => {
196
+ setMounted(true);
197
+ }, []); // Combine refs
187
198
 
188
199
  const combinedRef = React.useCallback(node => {
189
200
  menuRef.current = node;
@@ -206,8 +217,8 @@ const BaseMenu = /*#__PURE__*/React__default["default"].forwardRef((_ref2, ref)
206
217
  return _objectSpread(_objectSpread({}, sxStyles), PaperProps.style);
207
218
  }, [PaperProps.sx, PaperProps.style, colors]); // Calculate position (only when standalone)
208
219
 
209
- React.useLayoutEffect(() => {
210
- if (isInsidePopover || !open || !anchorEl) return; // Safety check - make sure anchorEl is a valid DOM element
220
+ useIsomorphicLayoutEffect(() => {
221
+ if (!canUseDOM() || isInsidePopover || !open || !anchorEl) return; // Safety check - make sure anchorEl is a valid DOM element
211
222
 
212
223
  if (!(anchorEl instanceof Element)) return;
213
224
  const rect = anchorEl.getBoundingClientRect();
@@ -255,7 +266,7 @@ const BaseMenu = /*#__PURE__*/React__default["default"].forwardRef((_ref2, ref)
255
266
  }, [open, anchorEl, anchorOrigin, transformOrigin, isInsidePopover]); // Click outside and escape handling (only when standalone)
256
267
 
257
268
  React.useEffect(() => {
258
- if (isInsidePopover || !open) return;
269
+ if (!canUseDOM() || isInsidePopover || !open) return;
259
270
 
260
271
  const handleClickOutside = e => {
261
272
  // Don't close if clicking inside the menu itself
@@ -301,10 +312,9 @@ const BaseMenu = /*#__PURE__*/React__default["default"].forwardRef((_ref2, ref)
301
312
  }, [open, onClose, anchorEl, isInsidePopover, isClickInsideOverlay]); // Focus first item on open
302
313
 
303
314
  React.useEffect(() => {
304
- if (open && autoFocus && menuRef.current) {
305
- const firstItem = menuRef.current.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
306
- if (firstItem) firstItem.focus();
307
- }
315
+ if (!canUseDOM() || !open || !autoFocus || !menuRef.current) return;
316
+ const firstItem = menuRef.current.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
317
+ if (firstItem) firstItem.focus();
308
318
  }, [open, autoFocus]);
309
319
  if (!open && !keepMounted) return null;
310
320
 
@@ -353,7 +363,9 @@ const BaseMenu = /*#__PURE__*/React__default["default"].forwardRef((_ref2, ref)
353
363
  }, props), /*#__PURE__*/React__default["default"].createElement("ul", _extends__default["default"]({
354
364
  style: listStyle
355
365
  }, MenuListProps), children));
356
- if (disablePortal) return content;
366
+ if (disablePortal) return content; // SSR safety - don't render portal until mounted
367
+
368
+ if (!mounted) return null;
357
369
  return /*#__PURE__*/ReactDOM.createPortal(content, document.body);
358
370
  });
359
371
  BaseMenu.displayName = "BaseMenu";
@@ -449,8 +461,8 @@ const BaseTabs = /*#__PURE__*/React__default["default"].forwardRef((_ref4, ref)
449
461
  const colorValue = (colors === null || colors === void 0 ? void 0 : colors[indicatorColor]) || (colors === null || colors === void 0 ? void 0 : colors.ibisRed);
450
462
  typeof colorValue === "object" ? colorValue.main : colorValue || "ibisRed.main"; // Update indicator position
451
463
 
452
- React.useLayoutEffect(() => {
453
- if (!tabsRef.current) return;
464
+ useIsomorphicLayoutEffect(() => {
465
+ if (!canUseDOM() || !tabsRef.current) return;
454
466
  const tabs = tabsRef.current.querySelectorAll('[role="tab"]');
455
467
  const selectedTab = Array.from(tabs).find((tab, index) => {
456
468
  const tabValue = tab.dataset.value !== undefined ? tab.dataset.value : index;
@@ -774,6 +786,11 @@ const BaseSnackbar = /*#__PURE__*/React__default["default"].forwardRef((_ref8, r
774
786
  const {
775
787
  colors
776
788
  } = ThemeProvider.useTheme();
789
+ const [mounted, setMounted] = React.useState(false); // SSR safety
790
+
791
+ React.useEffect(() => {
792
+ setMounted(true);
793
+ }, []);
777
794
  const startTimer = React.useCallback(() => {
778
795
  if (autoHideDuration != null) {
779
796
  timerRef.current = setTimeout(() => {
@@ -796,7 +813,7 @@ const BaseSnackbar = /*#__PURE__*/React__default["default"].forwardRef((_ref8, r
796
813
  }, [open, startTimer, stopTimer]); // Pause timer on window blur
797
814
 
798
815
  React.useEffect(() => {
799
- if (disableWindowBlurListener || !open) return;
816
+ if (!canUseDOM() || disableWindowBlurListener || !open) return;
800
817
 
801
818
  const handleBlur = () => stopTimer();
802
819
 
@@ -812,7 +829,9 @@ const BaseSnackbar = /*#__PURE__*/React__default["default"].forwardRef((_ref8, r
812
829
  window.removeEventListener("focus", handleFocus);
813
830
  };
814
831
  }, [open, disableWindowBlurListener, startTimer, stopTimer]);
815
- if (!open) return null;
832
+ if (!open) return null; // SSR safety - don't render portal until mounted
833
+
834
+ if (!mounted || !canUseDOM()) return null;
816
835
 
817
836
  const positionStyles = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
818
837
  position: "fixed",
@@ -908,10 +927,15 @@ const BasePopover = /*#__PURE__*/React__default["default"].forwardRef((_ref9, re
908
927
  top: 0,
909
928
  left: 0
910
929
  });
930
+ const [mounted, setMounted] = React.useState(false);
911
931
  const {
912
932
  registerOverlay,
913
933
  isClickInsideOverlay
914
- } = useOverlayStack();
934
+ } = useOverlayStack(); // SSR safety
935
+
936
+ React.useEffect(() => {
937
+ setMounted(true);
938
+ }, []);
915
939
  const combinedRef = React.useCallback(node => {
916
940
  popoverRef.current = node;
917
941
 
@@ -933,8 +957,8 @@ const BasePopover = /*#__PURE__*/React__default["default"].forwardRef((_ref9, re
933
957
  return _objectSpread(_objectSpread({}, sxStyles), PaperProps.style);
934
958
  }, [PaperProps.sx, PaperProps.style, colors]); // Calculate position
935
959
 
936
- React.useLayoutEffect(() => {
937
- if (!open) return;
960
+ useIsomorphicLayoutEffect(() => {
961
+ if (!canUseDOM() || !open) return;
938
962
  let top = 0;
939
963
  let left = 0;
940
964
 
@@ -983,7 +1007,7 @@ const BasePopover = /*#__PURE__*/React__default["default"].forwardRef((_ref9, re
983
1007
  }, [open, anchorEl, anchorOrigin, transformOrigin, anchorPosition, anchorReference, marginThreshold]); // Click outside handling
984
1008
 
985
1009
  React.useEffect(() => {
986
- if (!open) return;
1010
+ if (!canUseDOM() || !open) return;
987
1011
 
988
1012
  const handleClickOutside = e => {
989
1013
  // Use a small delay to ensure ref is populated after portal render
@@ -1056,7 +1080,9 @@ const BasePopover = /*#__PURE__*/React__default["default"].forwardRef((_ref9, re
1056
1080
  style: popoverStyle,
1057
1081
  onClick: onClick
1058
1082
  }, props), children));
1059
- if (disablePortal) return content;
1083
+ if (disablePortal) return content; // SSR safety - don't render portal until mounted
1084
+
1085
+ if (!mounted) return null;
1060
1086
  return /*#__PURE__*/ReactDOM.createPortal(content, document.body);
1061
1087
  });
1062
1088
  BasePopover.displayName = "BasePopover";
@@ -1088,10 +1114,15 @@ const BaseDrawer = /*#__PURE__*/React__default["default"].forwardRef((_ref10, re
1088
1114
  colors
1089
1115
  } = ThemeProvider.useTheme();
1090
1116
  const [isVisible, setIsVisible] = React.useState(open);
1117
+ const [mounted, setMounted] = React.useState(false);
1091
1118
  const {
1092
1119
  registerOverlay
1093
1120
  } = useOverlayStack();
1094
- const drawerRef = React.useRef(null); // Resolve PaperProps.sx colors
1121
+ const drawerRef = React.useRef(null); // SSR safety
1122
+
1123
+ React.useEffect(() => {
1124
+ setMounted(true);
1125
+ }, []); // Resolve PaperProps.sx colors
1095
1126
 
1096
1127
  const resolvedPaperStyle = React.useMemo(() => {
1097
1128
  const sxStyles = PaperProps.sx ? resolveStyleColors(PaperProps.sx, colors) : {};
@@ -1114,13 +1145,12 @@ const BaseDrawer = /*#__PURE__*/React__default["default"].forwardRef((_ref10, re
1114
1145
  }, [open, transitionDuration]); // Lock body scroll for temporary drawers
1115
1146
 
1116
1147
  React.useEffect(() => {
1117
- if (open && variant === "temporary") {
1118
- const originalOverflow = document.body.style.overflow;
1119
- document.body.style.overflow = "hidden";
1120
- return () => {
1121
- document.body.style.overflow = originalOverflow;
1122
- };
1123
- }
1148
+ if (!canUseDOM() || !open || variant !== "temporary") return;
1149
+ const originalOverflow = document.body.style.overflow;
1150
+ document.body.style.overflow = "hidden";
1151
+ return () => {
1152
+ document.body.style.overflow = originalOverflow;
1153
+ };
1124
1154
  }, [open, variant]);
1125
1155
  const drawerWidth = (resolvedPaperStyle === null || resolvedPaperStyle === void 0 ? void 0 : resolvedPaperStyle.width) || ((_PaperProps$style = PaperProps.style) === null || _PaperProps$style === void 0 ? void 0 : _PaperProps$style.width) || "240px";
1126
1156
  const drawerHeight = (resolvedPaperStyle === null || resolvedPaperStyle === void 0 ? void 0 : resolvedPaperStyle.height) || ((_PaperProps$style2 = PaperProps.style) === null || _PaperProps$style2 === void 0 ? void 0 : _PaperProps$style2.height) || "auto";
@@ -1205,7 +1235,9 @@ const BaseDrawer = /*#__PURE__*/React__default["default"].forwardRef((_ref10, re
1205
1235
  } // Temporary drawer - with backdrop
1206
1236
 
1207
1237
 
1208
- if (!isVisible && !open) return null;
1238
+ if (!isVisible && !open) return null; // SSR safety - don't render portal until mounted
1239
+
1240
+ if (!mounted || !canUseDOM()) return null;
1209
1241
 
1210
1242
  const backdropStyle = _objectSpread({
1211
1243
  position: "fixed",
@@ -1269,11 +1301,16 @@ const BaseTooltip = /*#__PURE__*/React__default["default"].forwardRef((_ref11, r
1269
1301
  top: 0,
1270
1302
  left: 0
1271
1303
  });
1304
+ const [mounted, setMounted] = React.useState(false);
1272
1305
  const anchorRef = React.useRef(null);
1273
1306
  const tooltipRef = React.useRef(null);
1274
1307
  const enterTimeoutRef = React.useRef(null);
1275
1308
  const leaveTimeoutRef = React.useRef(null);
1276
- const open = controlledOpen !== undefined ? controlledOpen : isOpen; // Merge slotProps and componentsProps
1309
+ const open = controlledOpen !== undefined ? controlledOpen : isOpen; // SSR safety
1310
+
1311
+ React.useEffect(() => {
1312
+ setMounted(true);
1313
+ }, []); // Merge slotProps and componentsProps
1277
1314
 
1278
1315
  const mergedSlotProps = _objectSpread(_objectSpread({}, componentsProps), slotProps); // Resolve tooltip styles
1279
1316
 
@@ -1309,7 +1346,7 @@ const BaseTooltip = /*#__PURE__*/React__default["default"].forwardRef((_ref11, r
1309
1346
  return _objectSpread(_objectSpread(_objectSpread({}, resolvedSx), popperSlotProps.style), popperPropsSx);
1310
1347
  }, [mergedSlotProps, PopperProps, colors]);
1311
1348
  const updatePosition = React.useCallback(() => {
1312
- if (!anchorRef.current || !tooltipRef.current) return;
1349
+ if (!canUseDOM() || !anchorRef.current || !tooltipRef.current) return;
1313
1350
  const anchorRect = anchorRef.current.getBoundingClientRect();
1314
1351
  const tooltipRect = tooltipRef.current.getBoundingClientRect();
1315
1352
  const offset = 8;
@@ -1455,8 +1492,9 @@ const BaseTooltip = /*#__PURE__*/React__default["default"].forwardRef((_ref11, r
1455
1492
  }
1456
1493
 
1457
1494
  const clonedChild = /*#__PURE__*/React__default["default"].cloneElement(childElement, childProps);
1458
- if (!title) return clonedChild;
1459
- const tooltipContent = open ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React__default["default"].createElement("div", {
1495
+ if (!title) return clonedChild; // SSR safety - don't render portal until mounted
1496
+
1497
+ const tooltipContent = open && mounted && canUseDOM() ? /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React__default["default"].createElement("div", {
1460
1498
  ref: tooltipRef,
1461
1499
  role: "tooltip",
1462
1500
  style: _objectSpread(_objectSpread({}, tooltipStyle), popperStyle)
@@ -21,10 +21,14 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
21
21
 
22
22
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty__default["default"](target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
23
23
 
24
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined'; // Global style cache to avoid re-injecting same styles
25
+
26
+
24
27
  const styleCache = new Map();
25
28
  let styleSheet = null;
26
29
 
27
30
  function getStyleSheet() {
31
+ if (!canUseDOM()) return null;
28
32
  if (styleSheet) return styleSheet; // Check for existing stylesheet
29
33
 
30
34
  const existing = document.querySelector("style[data-blerp-sx]");
@@ -43,6 +47,8 @@ function getStyleSheet() {
43
47
  }
44
48
 
45
49
  function injectStyles(className, pseudoStyles, nestedStyles, mediaQueries, themeColors) {
50
+ if (!canUseDOM()) return;
51
+
46
52
  if (!className) {
47
53
  return;
48
54
  }
@@ -65,7 +71,8 @@ function injectStyles(className, pseudoStyles, nestedStyles, mediaQueries, theme
65
71
 
66
72
  if (css) {
67
73
  try {
68
- const sheet = getStyleSheet(); // Split by newlines and insert each rule
74
+ const sheet = getStyleSheet();
75
+ if (!sheet) return; // Split by newlines and insert each rule
69
76
 
70
77
  css.split("\n").filter(Boolean).forEach(rule => {
71
78
  try {
@@ -137,12 +144,12 @@ function withSx(Component) {
137
144
  }, [sx, style, colors]); // Inject special styles (pseudo, nested, media queries)
138
145
 
139
146
  React.useEffect(() => {
140
- if (typeof window !== "undefined" && pseudoClassName) {
147
+ if (canUseDOM() && pseudoClassName) {
141
148
  injectStyles(pseudoClassName, pseudoStyles, nestedStyles, mediaQueries, colors);
142
149
  }
143
150
  }, [pseudoClassName, pseudoStyles, nestedStyles, mediaQueries, colors]); // Also inject synchronously for SSR/initial render
144
151
 
145
- if (typeof window !== "undefined" && pseudoClassName) {
152
+ if (canUseDOM() && pseudoClassName) {
146
153
  injectStyles(pseudoClassName, pseudoStyles, nestedStyles, mediaQueries, colors);
147
154
  } // Combine classNames
148
155
 
@@ -11,10 +11,13 @@ const _excluded = ["options", "value", "defaultValue", "onChange", "onInputChang
11
11
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
12
12
 
13
13
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
14
+
15
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined';
14
16
  /**
15
17
  * Autocomplete - combo box with filtering
16
18
  */
17
19
 
20
+
18
21
  const AutocompleteBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
19
22
  let {
20
23
  options = [],
@@ -163,6 +166,8 @@ const AutocompleteBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
163
166
 
164
167
 
165
168
  useEffect(() => {
169
+ if (!canUseDOM()) return;
170
+
166
171
  const handleClickOutside = event => {
167
172
  if (inputRef.current && !inputRef.current.contains(event.target) && listboxRef.current && !listboxRef.current.contains(event.target)) {
168
173
  handleClose();
@@ -233,6 +233,11 @@ const BaseModal = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
233
233
 
234
234
  if (disablePortal) {
235
235
  return modalContent;
236
+ } // SSR check - don't use createPortal on the server
237
+
238
+
239
+ if (typeof window === 'undefined') {
240
+ return null;
236
241
  }
237
242
 
238
243
  return /*#__PURE__*/createPortal(modalContent, container || document.body);
@@ -39,6 +39,8 @@ const resolveStyles = (styleObj, colors) => {
39
39
 
40
40
  return resolved;
41
41
  };
42
+
43
+ const canUseDOM = () => typeof window !== "undefined" && typeof document !== "undefined";
42
44
  /**
43
45
  * Input/TextField - Form input component (replaces MUI TextField)
44
46
  * Supports variants: outlined, filled, standard
@@ -329,7 +331,7 @@ const BaseInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
329
331
  const handleClick = e => {
330
332
  onClick === null || onClick === void 0 ? void 0 : onClick(e);
331
333
 
332
- if (!disabled) {
334
+ if (!disabled && canUseDOM()) {
333
335
  const input = document.getElementById(id);
334
336
  input === null || input === void 0 ? void 0 : input.focus();
335
337
  }
@@ -727,6 +727,7 @@ const AccordionSummary = /*#__PURE__*/React.forwardRef((_ref18, ref) => {
727
727
  style: iconStyle
728
728
  }, expandIcon));
729
729
  });
730
+ AccordionSummary.displayName = "AccordionSummary";
730
731
  /**
731
732
  * AccordionDetails - Content area
732
733
  */
@@ -1,7 +1,7 @@
1
1
  import _extends from '@babel/runtime/helpers/extends';
2
2
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
3
3
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
4
- import React, { useState, useRef, useCallback, useLayoutEffect, useEffect } from 'react';
4
+ import React, { useState, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import { withSx } from './withSx.js';
7
7
  import { useTheme } from './ThemeProvider.js';
@@ -18,6 +18,11 @@ const _excluded = ["orientation", "variant", "flexItem", "light", "textAlign", "
18
18
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
19
19
 
20
20
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
21
+
22
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined'; // Use useLayoutEffect only on client, useEffect on server
23
+
24
+
25
+ const useIsomorphicLayoutEffect = canUseDOM() ? useLayoutEffect : useEffect;
21
26
  /**
22
27
  * Divider - Visual separator (replaces MUI Divider)
23
28
  */
@@ -460,13 +465,18 @@ const BaseTooltip = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
460
465
  top: 0,
461
466
  left: 0
462
467
  });
468
+ const [mounted, setMounted] = useState(false);
463
469
  const timeoutRef = useRef(null);
464
470
  const childRef = useRef(null);
465
471
  const tooltipRef = useRef(null);
466
- const open = controlledOpen !== undefined ? controlledOpen : isOpen; // Calculate position based on child element and placement
472
+ const open = controlledOpen !== undefined ? controlledOpen : isOpen; // SSR safety - only mount portal after hydration
473
+
474
+ useEffect(() => {
475
+ setMounted(true);
476
+ }, []); // Calculate position based on child element and placement
467
477
 
468
478
  const updatePosition = useCallback(() => {
469
- if (!childRef.current) return;
479
+ if (!canUseDOM() || !childRef.current) return;
470
480
  const childRect = childRef.current.getBoundingClientRect();
471
481
  const tooltipEl = tooltipRef.current;
472
482
  const tooltipRect = tooltipEl ? tooltipEl.getBoundingClientRect() : {
@@ -566,17 +576,16 @@ const BaseTooltip = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
566
576
  });
567
577
  }, [placement]); // Update position when open changes or on scroll/resize
568
578
 
569
- useLayoutEffect(() => {
570
- if (open) {
571
- updatePosition(); // Update position on scroll and resize
579
+ useIsomorphicLayoutEffect(() => {
580
+ if (!canUseDOM() || !open) return;
581
+ updatePosition(); // Update position on scroll and resize
572
582
 
573
- window.addEventListener('scroll', updatePosition, true);
574
- window.addEventListener('resize', updatePosition);
575
- return () => {
576
- window.removeEventListener('scroll', updatePosition, true);
577
- window.removeEventListener('resize', updatePosition);
578
- };
579
- }
583
+ window.addEventListener('scroll', updatePosition, true);
584
+ window.addEventListener('resize', updatePosition);
585
+ return () => {
586
+ window.removeEventListener('scroll', updatePosition, true);
587
+ window.removeEventListener('resize', updatePosition);
588
+ };
580
589
  }, [open, updatePosition]); // Re-calculate after tooltip renders (to get accurate dimensions)
581
590
 
582
591
  useEffect(() => {
@@ -677,9 +686,10 @@ const BaseTooltip = /*#__PURE__*/React.forwardRef((_ref5, ref) => {
677
686
  }, (_componentsProps$tool3 = componentsProps.tooltip) === null || _componentsProps$tool3 === void 0 ? void 0 : _componentsProps$tool3.sx), PopperProps.sx), {}, {
678
687
  // Force background at the end to ensure it's never transparent
679
688
  backgroundColor
680
- });
689
+ }); // Don't render portal during SSR or before mount
690
+
681
691
 
682
- const tooltipContent = open && title ? /*#__PURE__*/React.createElement("span", {
692
+ const tooltipContent = open && title && mounted && canUseDOM() ? /*#__PURE__*/React.createElement("span", {
683
693
  ref: tooltipRef,
684
694
  role: "tooltip",
685
695
  style: tooltipStyle
@@ -1,7 +1,7 @@
1
1
  import _extends from '@babel/runtime/helpers/extends';
2
2
  import _defineProperty from '@babel/runtime/helpers/defineProperty';
3
3
  import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties';
4
- import React, { useContext, useRef, useState, useCallback, useEffect, useMemo, useLayoutEffect, createContext } from 'react';
4
+ import React, { useContext, useRef, useState, useEffect, useCallback, useMemo, useLayoutEffect, createContext } from 'react';
5
5
  import { createPortal } from 'react-dom';
6
6
  import { withSx } from './withSx.js';
7
7
  import { useTheme } from './ThemeProvider.js';
@@ -21,6 +21,11 @@ const _excluded = ["open", "onClose", "anchorEl", "anchorOrigin", "transformOrig
21
21
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
22
22
 
23
23
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
24
+
25
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined'; // Use useLayoutEffect only on client, useEffect on server
26
+
27
+
28
+ const useIsomorphicLayoutEffect = canUseDOM() ? useLayoutEffect : useEffect;
24
29
  /**
25
30
  * Context to detect if we're inside a Popover
26
31
  */
@@ -95,6 +100,7 @@ function resolveStyleColors(style, colors) {
95
100
 
96
101
 
97
102
  function isClickInsideAnyOverlay(target, excludeElement) {
103
+ if (!canUseDOM()) return false;
98
104
  let element = target;
99
105
 
100
106
  while (element && element !== document.body) {
@@ -169,10 +175,15 @@ const BaseMenu = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
169
175
  top: 0,
170
176
  left: 0
171
177
  });
178
+ const [mounted, setMounted] = useState(false);
172
179
  const {
173
180
  registerOverlay,
174
181
  isClickInsideOverlay
175
- } = useOverlayStack(); // Combine refs
182
+ } = useOverlayStack(); // SSR safety
183
+
184
+ useEffect(() => {
185
+ setMounted(true);
186
+ }, []); // Combine refs
176
187
 
177
188
  const combinedRef = useCallback(node => {
178
189
  menuRef.current = node;
@@ -195,8 +206,8 @@ const BaseMenu = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
195
206
  return _objectSpread(_objectSpread({}, sxStyles), PaperProps.style);
196
207
  }, [PaperProps.sx, PaperProps.style, colors]); // Calculate position (only when standalone)
197
208
 
198
- useLayoutEffect(() => {
199
- if (isInsidePopover || !open || !anchorEl) return; // Safety check - make sure anchorEl is a valid DOM element
209
+ useIsomorphicLayoutEffect(() => {
210
+ if (!canUseDOM() || isInsidePopover || !open || !anchorEl) return; // Safety check - make sure anchorEl is a valid DOM element
200
211
 
201
212
  if (!(anchorEl instanceof Element)) return;
202
213
  const rect = anchorEl.getBoundingClientRect();
@@ -244,7 +255,7 @@ const BaseMenu = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
244
255
  }, [open, anchorEl, anchorOrigin, transformOrigin, isInsidePopover]); // Click outside and escape handling (only when standalone)
245
256
 
246
257
  useEffect(() => {
247
- if (isInsidePopover || !open) return;
258
+ if (!canUseDOM() || isInsidePopover || !open) return;
248
259
 
249
260
  const handleClickOutside = e => {
250
261
  // Don't close if clicking inside the menu itself
@@ -290,10 +301,9 @@ const BaseMenu = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
290
301
  }, [open, onClose, anchorEl, isInsidePopover, isClickInsideOverlay]); // Focus first item on open
291
302
 
292
303
  useEffect(() => {
293
- if (open && autoFocus && menuRef.current) {
294
- const firstItem = menuRef.current.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
295
- if (firstItem) firstItem.focus();
296
- }
304
+ if (!canUseDOM() || !open || !autoFocus || !menuRef.current) return;
305
+ const firstItem = menuRef.current.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
306
+ if (firstItem) firstItem.focus();
297
307
  }, [open, autoFocus]);
298
308
  if (!open && !keepMounted) return null;
299
309
 
@@ -342,7 +352,9 @@ const BaseMenu = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
342
352
  }, props), /*#__PURE__*/React.createElement("ul", _extends({
343
353
  style: listStyle
344
354
  }, MenuListProps), children));
345
- if (disablePortal) return content;
355
+ if (disablePortal) return content; // SSR safety - don't render portal until mounted
356
+
357
+ if (!mounted) return null;
346
358
  return /*#__PURE__*/createPortal(content, document.body);
347
359
  });
348
360
  BaseMenu.displayName = "BaseMenu";
@@ -438,8 +450,8 @@ const BaseTabs = /*#__PURE__*/React.forwardRef((_ref4, ref) => {
438
450
  const colorValue = (colors === null || colors === void 0 ? void 0 : colors[indicatorColor]) || (colors === null || colors === void 0 ? void 0 : colors.ibisRed);
439
451
  typeof colorValue === "object" ? colorValue.main : colorValue || "ibisRed.main"; // Update indicator position
440
452
 
441
- useLayoutEffect(() => {
442
- if (!tabsRef.current) return;
453
+ useIsomorphicLayoutEffect(() => {
454
+ if (!canUseDOM() || !tabsRef.current) return;
443
455
  const tabs = tabsRef.current.querySelectorAll('[role="tab"]');
444
456
  const selectedTab = Array.from(tabs).find((tab, index) => {
445
457
  const tabValue = tab.dataset.value !== undefined ? tab.dataset.value : index;
@@ -763,6 +775,11 @@ const BaseSnackbar = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
763
775
  const {
764
776
  colors
765
777
  } = useTheme();
778
+ const [mounted, setMounted] = useState(false); // SSR safety
779
+
780
+ useEffect(() => {
781
+ setMounted(true);
782
+ }, []);
766
783
  const startTimer = useCallback(() => {
767
784
  if (autoHideDuration != null) {
768
785
  timerRef.current = setTimeout(() => {
@@ -785,7 +802,7 @@ const BaseSnackbar = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
785
802
  }, [open, startTimer, stopTimer]); // Pause timer on window blur
786
803
 
787
804
  useEffect(() => {
788
- if (disableWindowBlurListener || !open) return;
805
+ if (!canUseDOM() || disableWindowBlurListener || !open) return;
789
806
 
790
807
  const handleBlur = () => stopTimer();
791
808
 
@@ -801,7 +818,9 @@ const BaseSnackbar = /*#__PURE__*/React.forwardRef((_ref8, ref) => {
801
818
  window.removeEventListener("focus", handleFocus);
802
819
  };
803
820
  }, [open, disableWindowBlurListener, startTimer, stopTimer]);
804
- if (!open) return null;
821
+ if (!open) return null; // SSR safety - don't render portal until mounted
822
+
823
+ if (!mounted || !canUseDOM()) return null;
805
824
 
806
825
  const positionStyles = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({
807
826
  position: "fixed",
@@ -897,10 +916,15 @@ const BasePopover = /*#__PURE__*/React.forwardRef((_ref9, ref) => {
897
916
  top: 0,
898
917
  left: 0
899
918
  });
919
+ const [mounted, setMounted] = useState(false);
900
920
  const {
901
921
  registerOverlay,
902
922
  isClickInsideOverlay
903
- } = useOverlayStack();
923
+ } = useOverlayStack(); // SSR safety
924
+
925
+ useEffect(() => {
926
+ setMounted(true);
927
+ }, []);
904
928
  const combinedRef = useCallback(node => {
905
929
  popoverRef.current = node;
906
930
 
@@ -922,8 +946,8 @@ const BasePopover = /*#__PURE__*/React.forwardRef((_ref9, ref) => {
922
946
  return _objectSpread(_objectSpread({}, sxStyles), PaperProps.style);
923
947
  }, [PaperProps.sx, PaperProps.style, colors]); // Calculate position
924
948
 
925
- useLayoutEffect(() => {
926
- if (!open) return;
949
+ useIsomorphicLayoutEffect(() => {
950
+ if (!canUseDOM() || !open) return;
927
951
  let top = 0;
928
952
  let left = 0;
929
953
 
@@ -972,7 +996,7 @@ const BasePopover = /*#__PURE__*/React.forwardRef((_ref9, ref) => {
972
996
  }, [open, anchorEl, anchorOrigin, transformOrigin, anchorPosition, anchorReference, marginThreshold]); // Click outside handling
973
997
 
974
998
  useEffect(() => {
975
- if (!open) return;
999
+ if (!canUseDOM() || !open) return;
976
1000
 
977
1001
  const handleClickOutside = e => {
978
1002
  // Use a small delay to ensure ref is populated after portal render
@@ -1045,7 +1069,9 @@ const BasePopover = /*#__PURE__*/React.forwardRef((_ref9, ref) => {
1045
1069
  style: popoverStyle,
1046
1070
  onClick: onClick
1047
1071
  }, props), children));
1048
- if (disablePortal) return content;
1072
+ if (disablePortal) return content; // SSR safety - don't render portal until mounted
1073
+
1074
+ if (!mounted) return null;
1049
1075
  return /*#__PURE__*/createPortal(content, document.body);
1050
1076
  });
1051
1077
  BasePopover.displayName = "BasePopover";
@@ -1077,10 +1103,15 @@ const BaseDrawer = /*#__PURE__*/React.forwardRef((_ref10, ref) => {
1077
1103
  colors
1078
1104
  } = useTheme();
1079
1105
  const [isVisible, setIsVisible] = useState(open);
1106
+ const [mounted, setMounted] = useState(false);
1080
1107
  const {
1081
1108
  registerOverlay
1082
1109
  } = useOverlayStack();
1083
- const drawerRef = useRef(null); // Resolve PaperProps.sx colors
1110
+ const drawerRef = useRef(null); // SSR safety
1111
+
1112
+ useEffect(() => {
1113
+ setMounted(true);
1114
+ }, []); // Resolve PaperProps.sx colors
1084
1115
 
1085
1116
  const resolvedPaperStyle = useMemo(() => {
1086
1117
  const sxStyles = PaperProps.sx ? resolveStyleColors(PaperProps.sx, colors) : {};
@@ -1103,13 +1134,12 @@ const BaseDrawer = /*#__PURE__*/React.forwardRef((_ref10, ref) => {
1103
1134
  }, [open, transitionDuration]); // Lock body scroll for temporary drawers
1104
1135
 
1105
1136
  useEffect(() => {
1106
- if (open && variant === "temporary") {
1107
- const originalOverflow = document.body.style.overflow;
1108
- document.body.style.overflow = "hidden";
1109
- return () => {
1110
- document.body.style.overflow = originalOverflow;
1111
- };
1112
- }
1137
+ if (!canUseDOM() || !open || variant !== "temporary") return;
1138
+ const originalOverflow = document.body.style.overflow;
1139
+ document.body.style.overflow = "hidden";
1140
+ return () => {
1141
+ document.body.style.overflow = originalOverflow;
1142
+ };
1113
1143
  }, [open, variant]);
1114
1144
  const drawerWidth = (resolvedPaperStyle === null || resolvedPaperStyle === void 0 ? void 0 : resolvedPaperStyle.width) || ((_PaperProps$style = PaperProps.style) === null || _PaperProps$style === void 0 ? void 0 : _PaperProps$style.width) || "240px";
1115
1145
  const drawerHeight = (resolvedPaperStyle === null || resolvedPaperStyle === void 0 ? void 0 : resolvedPaperStyle.height) || ((_PaperProps$style2 = PaperProps.style) === null || _PaperProps$style2 === void 0 ? void 0 : _PaperProps$style2.height) || "auto";
@@ -1194,7 +1224,9 @@ const BaseDrawer = /*#__PURE__*/React.forwardRef((_ref10, ref) => {
1194
1224
  } // Temporary drawer - with backdrop
1195
1225
 
1196
1226
 
1197
- if (!isVisible && !open) return null;
1227
+ if (!isVisible && !open) return null; // SSR safety - don't render portal until mounted
1228
+
1229
+ if (!mounted || !canUseDOM()) return null;
1198
1230
 
1199
1231
  const backdropStyle = _objectSpread({
1200
1232
  position: "fixed",
@@ -1258,11 +1290,16 @@ const BaseTooltip = /*#__PURE__*/React.forwardRef((_ref11, ref) => {
1258
1290
  top: 0,
1259
1291
  left: 0
1260
1292
  });
1293
+ const [mounted, setMounted] = useState(false);
1261
1294
  const anchorRef = useRef(null);
1262
1295
  const tooltipRef = useRef(null);
1263
1296
  const enterTimeoutRef = useRef(null);
1264
1297
  const leaveTimeoutRef = useRef(null);
1265
- const open = controlledOpen !== undefined ? controlledOpen : isOpen; // Merge slotProps and componentsProps
1298
+ const open = controlledOpen !== undefined ? controlledOpen : isOpen; // SSR safety
1299
+
1300
+ useEffect(() => {
1301
+ setMounted(true);
1302
+ }, []); // Merge slotProps and componentsProps
1266
1303
 
1267
1304
  const mergedSlotProps = _objectSpread(_objectSpread({}, componentsProps), slotProps); // Resolve tooltip styles
1268
1305
 
@@ -1298,7 +1335,7 @@ const BaseTooltip = /*#__PURE__*/React.forwardRef((_ref11, ref) => {
1298
1335
  return _objectSpread(_objectSpread(_objectSpread({}, resolvedSx), popperSlotProps.style), popperPropsSx);
1299
1336
  }, [mergedSlotProps, PopperProps, colors]);
1300
1337
  const updatePosition = useCallback(() => {
1301
- if (!anchorRef.current || !tooltipRef.current) return;
1338
+ if (!canUseDOM() || !anchorRef.current || !tooltipRef.current) return;
1302
1339
  const anchorRect = anchorRef.current.getBoundingClientRect();
1303
1340
  const tooltipRect = tooltipRef.current.getBoundingClientRect();
1304
1341
  const offset = 8;
@@ -1444,8 +1481,9 @@ const BaseTooltip = /*#__PURE__*/React.forwardRef((_ref11, ref) => {
1444
1481
  }
1445
1482
 
1446
1483
  const clonedChild = /*#__PURE__*/React.cloneElement(childElement, childProps);
1447
- if (!title) return clonedChild;
1448
- const tooltipContent = open ? /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement("div", {
1484
+ if (!title) return clonedChild; // SSR safety - don't render portal until mounted
1485
+
1486
+ const tooltipContent = open && mounted && canUseDOM() ? /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement("div", {
1449
1487
  ref: tooltipRef,
1450
1488
  role: "tooltip",
1451
1489
  style: _objectSpread(_objectSpread({}, tooltipStyle), popperStyle)
@@ -11,10 +11,14 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
11
11
 
12
12
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
13
13
 
14
+ const canUseDOM = () => typeof window !== 'undefined' && typeof document !== 'undefined'; // Global style cache to avoid re-injecting same styles
15
+
16
+
14
17
  const styleCache = new Map();
15
18
  let styleSheet = null;
16
19
 
17
20
  function getStyleSheet() {
21
+ if (!canUseDOM()) return null;
18
22
  if (styleSheet) return styleSheet; // Check for existing stylesheet
19
23
 
20
24
  const existing = document.querySelector("style[data-blerp-sx]");
@@ -33,6 +37,8 @@ function getStyleSheet() {
33
37
  }
34
38
 
35
39
  function injectStyles(className, pseudoStyles, nestedStyles, mediaQueries, themeColors) {
40
+ if (!canUseDOM()) return;
41
+
36
42
  if (!className) {
37
43
  return;
38
44
  }
@@ -55,7 +61,8 @@ function injectStyles(className, pseudoStyles, nestedStyles, mediaQueries, theme
55
61
 
56
62
  if (css) {
57
63
  try {
58
- const sheet = getStyleSheet(); // Split by newlines and insert each rule
64
+ const sheet = getStyleSheet();
65
+ if (!sheet) return; // Split by newlines and insert each rule
59
66
 
60
67
  css.split("\n").filter(Boolean).forEach(rule => {
61
68
  try {
@@ -127,12 +134,12 @@ function withSx(Component) {
127
134
  }, [sx, style, colors]); // Inject special styles (pseudo, nested, media queries)
128
135
 
129
136
  useEffect(() => {
130
- if (typeof window !== "undefined" && pseudoClassName) {
137
+ if (canUseDOM() && pseudoClassName) {
131
138
  injectStyles(pseudoClassName, pseudoStyles, nestedStyles, mediaQueries, colors);
132
139
  }
133
140
  }, [pseudoClassName, pseudoStyles, nestedStyles, mediaQueries, colors]); // Also inject synchronously for SSR/initial render
134
141
 
135
- if (typeof window !== "undefined" && pseudoClassName) {
142
+ if (canUseDOM() && pseudoClassName) {
136
143
  injectStyles(pseudoClassName, pseudoStyles, nestedStyles, mediaQueries, colors);
137
144
  } // Combine classNames
138
145
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blerp/design",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "Blerp UI",
5
5
  "main": "dist/esm/index.js",
6
6
  "scripts": {