@app-studio/web 0.9.13 → 0.9.15

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.
@@ -5711,6 +5711,24 @@
5711
5711
  var {
5712
5712
  getColor
5713
5713
  } = appStudio.useTheme();
5714
+ // Use useElementPosition for intelligent tooltip positioning
5715
+ var {
5716
+ ref: positionRef,
5717
+ relation
5718
+ } = appStudio.useElementPosition({
5719
+ trackChanges: true,
5720
+ trackOnHover: true,
5721
+ trackOnScroll: true,
5722
+ trackOnResize: true
5723
+ });
5724
+ // Create a separate ref for the SVG element
5725
+ var chartRef = React.useRef(null);
5726
+ // Sync the position ref with the chart ref for positioning calculations
5727
+ React.useEffect(() => {
5728
+ if (chartRef.current && positionRef) {
5729
+ positionRef.current = chartRef.current;
5730
+ }
5731
+ }, [chartRef, positionRef]);
5714
5732
  // Calculate chart dimensions
5715
5733
  var size = Math.min(width, height);
5716
5734
  var radius = size / 2 * 0.8;
@@ -5779,12 +5797,30 @@
5779
5797
  return result;
5780
5798
  }, [dataPoints, total, radius, centerX, centerY, donutRadius, animationProgress, isDonut]);
5781
5799
  return /*#__PURE__*/React__default.createElement("svg", {
5800
+ ref: chartRef,
5782
5801
  width: width,
5783
5802
  height: height
5784
5803
  }, slices.map((slice, index) => {
5785
5804
  var handleMouseEnter = e => {
5786
5805
  var tooltipContent = slice.label + ": " + slice.value + " (" + slice.percentage + ")";
5787
- showTooltip(e.clientX, e.clientY, tooltipContent);
5806
+ // Use intelligent positioning based on useElementPosition relation data
5807
+ var x = e.clientX;
5808
+ var y = e.clientY;
5809
+ if (relation && chartRef.current) {
5810
+ var chartRect = chartRef.current.getBoundingClientRect();
5811
+ // Adjust tooltip position based on available space
5812
+ if (relation.space.horizontal === 'left') {
5813
+ x = e.clientX - 100; // Offset tooltip to the left
5814
+ } else {
5815
+ x = e.clientX + 10; // Offset tooltip to the right
5816
+ }
5817
+ if (relation.space.vertical === 'top') {
5818
+ y = e.clientY - 30; // Offset tooltip above
5819
+ } else {
5820
+ y = e.clientY + 10; // Offset tooltip below
5821
+ }
5822
+ }
5823
+ showTooltip(x, y, tooltipContent);
5788
5824
  };
5789
5825
  var handleClick = () => {
5790
5826
  if (onSliceClick) {
@@ -13080,6 +13116,16 @@
13080
13116
  var [mentionStartPos, setMentionStartPos] = React.useState(-1);
13081
13117
  var [selectedMentionIndex, setSelectedMentionIndex] = React.useState(-1);
13082
13118
  var [filteredMentions, setFilteredMentions] = React.useState([]);
13119
+ // Use useElementPosition for intelligent dropdown positioning
13120
+ var {
13121
+ ref: positionRef,
13122
+ relation
13123
+ } = appStudio.useElementPosition({
13124
+ trackChanges: true,
13125
+ trackOnHover: true,
13126
+ trackOnScroll: true,
13127
+ trackOnResize: true
13128
+ });
13083
13129
  // Positioning state for dropdowns
13084
13130
  var [mentionPosition, setMentionPosition] = React.useState({
13085
13131
  x: 0,
@@ -13089,7 +13135,6 @@
13089
13135
  x: 0,
13090
13136
  y: 0
13091
13137
  });
13092
- // Note: Using custom positioning logic for better control over dropdown placement
13093
13138
  // Update the content of the editable div when the value prop changes
13094
13139
  React.useEffect(() => {
13095
13140
  var editableDiv = ref;
@@ -13141,7 +13186,13 @@
13141
13186
  setMentionStartPos(-1);
13142
13187
  setSelectedMentionIndex(-1);
13143
13188
  }, [mentionData, mentionTrigger]);
13144
- // Calculate optimal position for dropdowns
13189
+ // Sync the position ref with the container ref for positioning calculations
13190
+ React.useEffect(() => {
13191
+ if (containerRef.current && positionRef) {
13192
+ positionRef.current = containerRef.current;
13193
+ }
13194
+ }, [containerRef, positionRef]);
13195
+ // Calculate optimal position for dropdowns using useElementPosition
13145
13196
  var calculateDropdownPosition = React.useCallback(function (dropdownHeight) {
13146
13197
  if (dropdownHeight === void 0) {
13147
13198
  dropdownHeight = 200;
@@ -13151,22 +13202,26 @@
13151
13202
  y: 0
13152
13203
  };
13153
13204
  var containerRect = containerRef.current.getBoundingClientRect();
13205
+ // Use relation data for intelligent positioning if available
13206
+ if (relation) {
13207
+ var _useTopPlacement = relation.space.vertical === 'top';
13208
+ return {
13209
+ x: containerRect.left,
13210
+ y: _useTopPlacement ? containerRect.top - dropdownHeight - 8 : containerRect.bottom + 8
13211
+ };
13212
+ }
13213
+ // Fallback to manual calculation if relation data is not available
13154
13214
  var viewportHeight = window.innerHeight;
13155
- var viewportWidth = window.innerWidth;
13156
- // Calculate available space
13157
13215
  var availableSpace = {
13158
13216
  top: containerRect.top,
13159
- bottom: viewportHeight - containerRect.bottom,
13160
- left: containerRect.left,
13161
- right: viewportWidth - containerRect.right
13217
+ bottom: viewportHeight - containerRect.bottom
13162
13218
  };
13163
- // Prefer bottom placement, but use top if not enough space
13164
13219
  var useTopPlacement = availableSpace.bottom < dropdownHeight + 8 && availableSpace.top > availableSpace.bottom;
13165
13220
  return {
13166
13221
  x: containerRect.left,
13167
13222
  y: useTopPlacement ? containerRect.top - dropdownHeight - 8 : containerRect.bottom + 8
13168
13223
  };
13169
- }, []);
13224
+ }, [relation]);
13170
13225
  // Handle focus events
13171
13226
  var handleFocus = React.useCallback(() => {
13172
13227
  setIsFocused(true);
@@ -13372,7 +13427,7 @@
13372
13427
  opacity: 0.7,
13373
13428
  padding: '4px'
13374
13429
  }
13375
- }, "Mentions (Trigger: ", mentionTrigger, ")"))))), showSuggestions && suggestions.length > 0 && !showMentions && isFocused && !value && (/*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
13430
+ }, "Mentions (Trigger: ", mentionTrigger, ")", relation && (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("br", null), "Space: ", relation.space.vertical, "-", relation.space.horizontal))))))), showSuggestions && suggestions.length > 0 && !showMentions && isFocused && !value && (/*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
13376
13431
  position: "fixed",
13377
13432
  left: suggestionPosition.x,
13378
13433
  top: suggestionPosition.y,
@@ -13418,7 +13473,7 @@
13418
13473
  opacity: 0.7,
13419
13474
  padding: '4px'
13420
13475
  }
13421
- }, "Suggestions (Focus-triggered)"))))));
13476
+ }, "Suggestions (Focus-triggered)", relation && (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("br", null), "Space: ", relation.space.vertical, "-", relation.space.horizontal))))))));
13422
13477
  });
13423
13478
  EditableInput.displayName = 'EditableInput';
13424
13479
 
@@ -17005,8 +17060,78 @@
17005
17060
  };
17006
17061
  };
17007
17062
 
17008
- var _excluded$T = ["children", "shape", "variant", "isHovered", "setIsHovered", "isDisabled", "isToggle", "setIsToggled", "onToggle", "views"];
17063
+ var ToggleShapes = {
17064
+ sharp: 0,
17065
+ rounded: 4,
17066
+ pillShaped: 24
17067
+ };
17068
+ /**
17069
+ * Generate toggle variants with proper color combinations based on main color and contrast
17070
+ * Similar to Button's getButtonVariants function
17071
+ */
17072
+ var getToggleVariants = (color, isLight) => ({
17073
+ outline: {
17074
+ backgroundColor: 'transparent',
17075
+ color: color,
17076
+ borderWidth: 1,
17077
+ borderStyle: 'solid',
17078
+ borderColor: color,
17079
+ _hover: {
17080
+ backgroundColor: color,
17081
+ color: isLight ? 'color.black' : 'color.white',
17082
+ transform: 'translateY(-1px)'
17083
+ },
17084
+ _active: {
17085
+ backgroundColor: color,
17086
+ color: isLight ? 'color.black' : 'color.white',
17087
+ transform: 'translateY(0)'
17088
+ },
17089
+ transition: 'all 0.2s ease'
17090
+ },
17091
+ ghost: {
17092
+ backgroundColor: 'transparent',
17093
+ color: color,
17094
+ borderWidth: 0,
17095
+ borderStyle: 'none',
17096
+ borderColor: 'transparent',
17097
+ _hover: {
17098
+ backgroundColor: color,
17099
+ color: isLight ? 'color.black' : 'color.white',
17100
+ transform: 'translateY(-1px)'
17101
+ },
17102
+ _active: {
17103
+ backgroundColor: color,
17104
+ color: isLight ? 'color.black' : 'color.white',
17105
+ transform: 'translateY(0)'
17106
+ },
17107
+ transition: 'all 0.2s ease'
17108
+ },
17109
+ link: {
17110
+ backgroundColor: 'transparent',
17111
+ color: color,
17112
+ borderWidth: 1,
17113
+ borderStyle: 'solid',
17114
+ borderColor: 'transparent',
17115
+ textDecoration: 'underline',
17116
+ textUnderlineOffset: '1px',
17117
+ textDecorationThickness: '1px',
17118
+ _hover: {
17119
+ borderColor: color,
17120
+ textDecorationThickness: '2px',
17121
+ transform: 'translateY(-1px)'
17122
+ },
17123
+ _active: {
17124
+ borderColor: color,
17125
+ textDecorationThickness: '2px',
17126
+ transform: 'translateY(0)'
17127
+ },
17128
+ transition: 'all 0.2s ease'
17129
+ }
17130
+ });
17131
+
17132
+ var _excluded$T = ["children", "shape", "variant", "isHovered", "setIsHovered", "isDisabled", "isToggle", "setIsToggled", "onToggle", "views", "backgroundColor", "color", "themeMode"];
17009
17133
  var ToggleView = _ref => {
17134
+ var _ref2;
17010
17135
  var {
17011
17136
  children,
17012
17137
  shape = 'rounded',
@@ -17017,25 +17142,30 @@
17017
17142
  isToggle,
17018
17143
  setIsToggled,
17019
17144
  onToggle,
17020
- views
17145
+ views,
17146
+ backgroundColor,
17147
+ // primary candidate for main color
17148
+ color,
17149
+ // 2nd candidate for main color
17150
+ themeMode: elementMode
17021
17151
  } = _ref,
17022
17152
  props = _objectWithoutPropertiesLoose(_ref, _excluded$T);
17023
- var toggleColor = !isDisabled ? 'color.trueGray.400' : 'theme.disabled';
17153
+ /* theme helpers */
17154
+ var {
17155
+ getColor,
17156
+ themeMode
17157
+ } = appStudio.useTheme();
17158
+ var mode = elementMode != null ? elementMode : themeMode;
17159
+ /* MAIN COLOR – determines the entire palette */
17160
+ var mainColorKey = (_ref2 = backgroundColor != null ? backgroundColor : color) != null ? _ref2 : 'theme.primary';
17161
+ var mainTone = getColor(isDisabled ? 'theme.disabled' : mainColorKey, {
17162
+ themeMode: mode
17163
+ });
17164
+ var tone = contrast(mainTone);
17165
+ /* variant palette */
17166
+ var palette = React.useMemo(() => getToggleVariants(mainTone, tone === 'light'), [mainTone, tone]);
17167
+ var base = palette[variant];
17024
17168
  var isActive = !!(isToggle || isHovered);
17025
- var toggleVariants = {
17026
- outline: {
17027
- borderWidth: 1,
17028
- borderStyle: 'solid',
17029
- borderColor: 'color.trueGray.400'
17030
- },
17031
- link: {
17032
- borderWidth: 1,
17033
- borderStyle: 'solid',
17034
- borderColor: isActive ? toggleColor : 'transparent',
17035
- textDecoration: 'underline'
17036
- },
17037
- ghost: {}
17038
- };
17039
17169
  var handleHover = () => setIsHovered(!isHovered);
17040
17170
  var handleToggle = () => {
17041
17171
  if (!isDisabled) {
@@ -17052,14 +17182,15 @@
17052
17182
  role: "Toggle",
17053
17183
  padding: shape === 'pillShaped' ? '10px 12px' : '8px',
17054
17184
  width: "fit-content",
17055
- color: isActive ? 'color.white' : toggleColor,
17056
- backgroundColor: isActive ? toggleColor : 'transparent',
17057
- onMouseEnter: handleHover,
17058
- onMouseLeave: () => setIsHovered(false),
17059
17185
  cursor: isDisabled ? 'not-allowed' : 'pointer',
17060
- borderRadius: shape === 'pillShaped' ? '50px' : '8px',
17061
- onClick: handleToggle
17062
- }, toggleVariants[variant], props, views == null ? void 0 : views.container), children);
17186
+ borderRadius: ToggleShapes[shape],
17187
+ onClick: handleToggle,
17188
+ onMouseEnter: handleHover,
17189
+ onMouseLeave: () => setIsHovered(false)
17190
+ }, base, isActive && {
17191
+ backgroundColor: mainTone,
17192
+ color: tone === 'light' ? 'color.black' : 'color.white'
17193
+ }, props, views == null ? void 0 : views.container), children);
17063
17194
  };
17064
17195
 
17065
17196
  var _excluded$U = ["children", "shape", "variant", "isDisabled", "isToggled", "onToggle"];
@@ -17484,109 +17615,103 @@
17484
17615
  triggerRef
17485
17616
  } = useDropdownMenuContext();
17486
17617
  var contentRef = React.useRef(null);
17618
+ // Use useElementPosition for intelligent positioning
17619
+ var {
17620
+ ref: positionRef,
17621
+ relation
17622
+ } = appStudio.useElementPosition({
17623
+ trackChanges: true,
17624
+ trackOnHover: true,
17625
+ trackOnScroll: true,
17626
+ trackOnResize: true
17627
+ });
17487
17628
  var [optimalPosition, setOptimalPosition] = React.useState({
17488
17629
  x: 0,
17489
17630
  y: 0,
17490
17631
  placement: side
17491
17632
  });
17492
- // Calculate optimal position when the dropdown opens
17633
+ // Sync the position ref with the trigger ref for positioning calculations
17634
+ React.useEffect(() => {
17635
+ if (triggerRef.current && positionRef) {
17636
+ positionRef.current = triggerRef.current;
17637
+ }
17638
+ }, [triggerRef, positionRef, isOpen]);
17639
+ // Calculate optimal position using useElementPosition when the dropdown opens
17493
17640
  React.useEffect(() => {
17494
17641
  if (isOpen && contentRef.current && triggerRef.current) {
17495
- var contentRect = contentRef.current.getBoundingClientRect();
17496
17642
  var triggerRect = triggerRef.current.getBoundingClientRect();
17497
- // Get content dimensions
17498
- var contentWidth = Math.max(contentRect.width || 180, 180);
17499
- var contentHeight = Math.max(contentRect.height || 100, 100);
17500
- // Get viewport dimensions
17501
- var viewportWidth = window.innerWidth;
17502
- var viewportHeight = window.innerHeight;
17503
- // Calculate available space on all sides from the trigger
17504
- var availableSpace = {
17505
- top: triggerRect.top,
17506
- right: viewportWidth - triggerRect.right,
17507
- bottom: viewportHeight - triggerRect.bottom,
17508
- left: triggerRect.left
17509
- };
17510
- // Determine optimal placement based on available space and preferred side
17511
- var placements = [{
17512
- placement: 'bottom',
17513
- space: availableSpace.bottom,
17514
- fits: availableSpace.bottom >= contentHeight + 8,
17515
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
17516
- y: triggerRect.bottom + 8
17517
- }, {
17518
- placement: 'top',
17519
- space: availableSpace.top,
17520
- fits: availableSpace.top >= contentHeight + 8,
17521
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
17522
- y: triggerRect.top - contentHeight - 8
17523
- }, {
17524
- placement: 'right',
17525
- space: availableSpace.right,
17526
- fits: availableSpace.right >= contentWidth + 8,
17527
- x: triggerRect.right + 8,
17528
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
17529
- }, {
17530
- placement: 'left',
17531
- space: availableSpace.left,
17532
- fits: availableSpace.left >= contentWidth + 8,
17533
- x: triggerRect.left - contentWidth - 8,
17534
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
17535
- }];
17536
- // First try the preferred side if it fits
17537
- var preferredPlacement = placements.find(p => p.placement === side && p.fits);
17538
- if (preferredPlacement) {
17539
- setOptimalPosition({
17540
- x: preferredPlacement.x,
17541
- y: preferredPlacement.y,
17542
- placement: preferredPlacement.placement
17543
- });
17544
- return;
17545
- }
17546
- // Otherwise, find the best fitting placement
17547
- var fittingPlacement = placements.find(p => p.fits);
17548
- if (fittingPlacement) {
17549
- setOptimalPosition({
17550
- x: fittingPlacement.x,
17551
- y: fittingPlacement.y,
17552
- placement: fittingPlacement.placement
17553
- });
17554
- return;
17555
- }
17556
- // If nothing fits, choose the placement with the most space
17557
- var bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
17558
- // Ensure the content stays within viewport bounds
17559
- var finalX = bestPlacement.x;
17560
- var finalY = bestPlacement.y;
17561
- if (finalX + contentWidth > viewportWidth) {
17562
- finalX = viewportWidth - contentWidth - 8;
17563
- }
17564
- if (finalX < 8) {
17565
- finalX = 8;
17643
+ var placement = side;
17644
+ // Use relation data to determine optimal placement
17645
+ if (relation) {
17646
+ // If preferred side doesn't have enough space, use the side with more space
17647
+ if (side === 'bottom' && relation.space.vertical === 'top') {
17648
+ placement = 'top';
17649
+ } else if (side === 'top' && relation.space.vertical === 'bottom') {
17650
+ placement = 'bottom';
17651
+ } else if (side === 'right' && relation.space.horizontal === 'left') {
17652
+ placement = 'left';
17653
+ } else if (side === 'left' && relation.space.horizontal === 'right') {
17654
+ placement = 'right';
17655
+ }
17566
17656
  }
17567
- if (finalY + contentHeight > viewportHeight) {
17568
- finalY = viewportHeight - contentHeight - 8;
17569
- }
17570
- if (finalY < 8) {
17571
- finalY = 8;
17657
+ // Calculate position based on optimal placement and alignment
17658
+ var x = 0;
17659
+ var y = 0;
17660
+ switch (placement) {
17661
+ case 'bottom':
17662
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 180 // Estimated content width
17663
+ : triggerRect.left + triggerRect.width / 2 - 90; // Half of estimated width
17664
+ y = triggerRect.bottom + 8;
17665
+ break;
17666
+ case 'top':
17667
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 180 : triggerRect.left + triggerRect.width / 2 - 90;
17668
+ y = triggerRect.top - 8; // Will be adjusted with transform
17669
+ break;
17670
+ case 'right':
17671
+ x = triggerRect.right + 8;
17672
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 100 // Estimated content height
17673
+ : triggerRect.top + triggerRect.height / 2 - 50; // Half of estimated height
17674
+ break;
17675
+ case 'left':
17676
+ x = triggerRect.left - 8; // Will be adjusted with transform
17677
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 100 : triggerRect.top + triggerRect.height / 2 - 50;
17678
+ break;
17572
17679
  }
17573
17680
  setOptimalPosition({
17574
- x: finalX,
17575
- y: finalY,
17576
- placement: bestPlacement.placement
17681
+ x,
17682
+ y,
17683
+ placement
17577
17684
  });
17578
17685
  }
17579
- }, [isOpen, side, align, triggerRef]);
17686
+ }, [isOpen, side, align, triggerRef, relation]);
17580
17687
  if (!isOpen) {
17581
17688
  return null;
17582
17689
  }
17583
- // Create intelligent positioning styles
17584
- var positionStyles = {
17585
- position: 'fixed',
17586
- left: optimalPosition.x,
17587
- top: optimalPosition.y,
17588
- zIndex: 1000
17690
+ // Create intelligent positioning styles with transform for better placement
17691
+ var getPositionStyles = () => {
17692
+ var baseStyles = {
17693
+ position: 'fixed',
17694
+ left: optimalPosition.x,
17695
+ top: optimalPosition.y,
17696
+ zIndex: 1000
17697
+ };
17698
+ // Add transform based on placement for better positioning
17699
+ switch (optimalPosition.placement) {
17700
+ case 'top':
17701
+ return Object.assign({}, baseStyles, {
17702
+ transform: 'translateY(-100%)'
17703
+ });
17704
+ case 'left':
17705
+ return Object.assign({}, baseStyles, {
17706
+ transform: 'translateX(-100%)'
17707
+ });
17708
+ case 'bottom':
17709
+ case 'right':
17710
+ default:
17711
+ return baseStyles;
17712
+ }
17589
17713
  };
17714
+ var positionStyles = getPositionStyles();
17590
17715
  return /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
17591
17716
  ref: contentRef,
17592
17717
  id: "dropdown-menu",
@@ -17606,7 +17731,13 @@
17606
17731
  item: item,
17607
17732
  views: views
17608
17733
  });
17609
- }));
17734
+ }), (/*#__PURE__*/React__default.createElement("div", {
17735
+ style: {
17736
+ fontSize: '10px',
17737
+ opacity: 0.7,
17738
+ padding: '4px'
17739
+ }
17740
+ }, "Placement: ", optimalPosition.placement, relation && (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("br", null), "Space: ", relation.space.vertical, "-", relation.space.horizontal)))));
17610
17741
  };
17611
17742
  // DropdownMenu Item component
17612
17743
  var DropdownMenuItem = _ref4 => {
@@ -20790,111 +20921,99 @@
20790
20921
  contentId,
20791
20922
  triggerId
20792
20923
  } = useHoverCardContext();
20793
- var [optimalPosition, setOptimalPosition] = React.useState({
20794
- x: 0,
20795
- y: 0,
20796
- placement: side
20924
+ // Use useElementPosition for intelligent positioning
20925
+ var {
20926
+ ref: positionRef,
20927
+ relation
20928
+ } = appStudio.useElementPosition({
20929
+ trackChanges: true,
20930
+ trackOnHover: true,
20931
+ trackOnScroll: true,
20932
+ trackOnResize: true
20797
20933
  });
20798
- // Calculate optimal position when the card opens or content dimensions change
20934
+ // Sync the position ref with the trigger ref for positioning calculations
20799
20935
  React.useEffect(() => {
20800
- if (isOpen && contentRef != null && contentRef.current && triggerRef != null && triggerRef.current) {
20801
- var contentRect = contentRef.current.getBoundingClientRect();
20802
- var triggerRect = triggerRef.current.getBoundingClientRect();
20803
- // Get content dimensions
20804
- var contentWidth = Math.max(contentRect.width || 200, 200);
20805
- var contentHeight = Math.max(contentRect.height || 100, 100);
20806
- // Get viewport dimensions
20807
- var viewportWidth = window.innerWidth;
20808
- var viewportHeight = window.innerHeight;
20809
- // Calculate available space on all sides from the trigger
20810
- var availableSpace = {
20811
- top: triggerRect.top,
20812
- right: viewportWidth - triggerRect.right,
20813
- bottom: viewportHeight - triggerRect.bottom,
20814
- left: triggerRect.left
20815
- };
20816
- // Determine optimal placement based on available space and preferred side
20817
- var placements = [{
20818
- placement: 'bottom',
20819
- space: availableSpace.bottom,
20820
- fits: availableSpace.bottom >= contentHeight + sideOffset,
20821
- x: triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
20822
- y: triggerRect.bottom + sideOffset
20823
- }, {
20824
- placement: 'top',
20825
- space: availableSpace.top,
20826
- fits: availableSpace.top >= contentHeight + sideOffset,
20827
- x: triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
20828
- y: triggerRect.top - contentHeight - sideOffset
20829
- }, {
20830
- placement: 'right',
20831
- space: availableSpace.right,
20832
- fits: availableSpace.right >= contentWidth + sideOffset,
20833
- x: triggerRect.right + sideOffset,
20834
- y: triggerRect.top + triggerRect.height / 2 - contentHeight / 2
20835
- }, {
20836
- placement: 'left',
20837
- space: availableSpace.left,
20838
- fits: availableSpace.left >= contentWidth + sideOffset,
20839
- x: triggerRect.left - contentWidth - sideOffset,
20840
- y: triggerRect.top + triggerRect.height / 2 - contentHeight / 2
20841
- }];
20842
- // First try the preferred side if it fits
20843
- var preferredPlacement = placements.find(p => p.placement === side && p.fits);
20844
- if (preferredPlacement) {
20845
- setOptimalPosition({
20846
- x: preferredPlacement.x,
20847
- y: preferredPlacement.y,
20848
- placement: preferredPlacement.placement
20849
- });
20850
- return;
20936
+ if (triggerRef != null && triggerRef.current && (positionRef == null ? void 0 : positionRef.current) !== triggerRef.current) {
20937
+ // Update the position tracking to use the trigger element
20938
+ if (positionRef) {
20939
+ positionRef.current = triggerRef.current;
20851
20940
  }
20852
- // Otherwise, find the best fitting placement
20853
- var fittingPlacement = placements.find(p => p.fits);
20854
- if (fittingPlacement) {
20855
- setOptimalPosition({
20856
- x: fittingPlacement.x,
20857
- y: fittingPlacement.y,
20858
- placement: fittingPlacement.placement
20859
- });
20860
- return;
20861
- }
20862
- // If nothing fits, choose the placement with the most space
20863
- var bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
20864
- // Ensure the content stays within viewport bounds
20865
- var finalX = bestPlacement.x;
20866
- var finalY = bestPlacement.y;
20867
- if (finalX + contentWidth > viewportWidth) {
20868
- finalX = viewportWidth - contentWidth - 8;
20869
- }
20870
- if (finalX < 8) {
20871
- finalX = 8;
20872
- }
20873
- if (finalY + contentHeight > viewportHeight) {
20874
- finalY = viewportHeight - contentHeight - 8;
20875
- }
20876
- if (finalY < 8) {
20877
- finalY = 8;
20878
- }
20879
- setOptimalPosition({
20880
- x: finalX,
20881
- y: finalY,
20882
- placement: bestPlacement.placement
20883
- });
20884
20941
  }
20885
- }, [isOpen, side, sideOffset, contentRef, triggerRef]);
20942
+ }, [triggerRef, positionRef, isOpen]);
20886
20943
  var handleMouseEnter = () => cancelCloseTimer(); // Keep card open if mouse enters content
20887
20944
  var handleMouseLeave = () => closeCard();
20888
20945
  if (!isOpen) {
20889
20946
  return null; // Don't render content if not open
20890
20947
  }
20891
- // Create intelligent positioning styles
20892
- var positionStyles = {
20893
- position: 'fixed',
20894
- left: optimalPosition.x,
20895
- top: optimalPosition.y,
20896
- zIndex: 1000
20948
+ // Create intelligent positioning styles based on useElementPosition relation data
20949
+ var getPositionStyles = () => {
20950
+ if (!relation || !(triggerRef != null && triggerRef.current)) {
20951
+ // Fallback positioning if relation data is not available
20952
+ return {
20953
+ position: 'absolute',
20954
+ top: 0,
20955
+ left: 0,
20956
+ zIndex: 1000
20957
+ };
20958
+ }
20959
+ var triggerRect = triggerRef.current.getBoundingClientRect();
20960
+ var placement = side;
20961
+ // Use relation data to determine optimal placement
20962
+ // If preferred side doesn't have enough space, use the side with more space
20963
+ if (side === 'bottom' && relation.space.vertical === 'top') {
20964
+ placement = 'top';
20965
+ } else if (side === 'top' && relation.space.vertical === 'bottom') {
20966
+ placement = 'bottom';
20967
+ } else if (side === 'right' && relation.space.horizontal === 'left') {
20968
+ placement = 'left';
20969
+ } else if (side === 'left' && relation.space.horizontal === 'right') {
20970
+ placement = 'right';
20971
+ }
20972
+ // Calculate position based on optimal placement
20973
+ var x = 0;
20974
+ var y = 0;
20975
+ switch (placement) {
20976
+ case 'bottom':
20977
+ x = triggerRect.left + triggerRect.width / 2;
20978
+ y = triggerRect.bottom + sideOffset;
20979
+ break;
20980
+ case 'top':
20981
+ x = triggerRect.left + triggerRect.width / 2;
20982
+ y = triggerRect.top - sideOffset;
20983
+ break;
20984
+ case 'right':
20985
+ x = triggerRect.right + sideOffset;
20986
+ y = triggerRect.top + triggerRect.height / 2;
20987
+ break;
20988
+ case 'left':
20989
+ x = triggerRect.left - sideOffset;
20990
+ y = triggerRect.top + triggerRect.height / 2;
20991
+ break;
20992
+ }
20993
+ return {
20994
+ position: 'fixed',
20995
+ left: x,
20996
+ top: y,
20997
+ zIndex: 1000,
20998
+ transform: getTransformOrigin(placement)
20999
+ };
20897
21000
  };
21001
+ // Helper function to set transform origin for better positioning
21002
+ var getTransformOrigin = placement => {
21003
+ switch (placement) {
21004
+ case 'bottom':
21005
+ return 'translate(-50%, 0)';
21006
+ case 'top':
21007
+ return 'translate(-50%, -100%)';
21008
+ case 'right':
21009
+ return 'translate(0, -50%)';
21010
+ case 'left':
21011
+ return 'translate(-100%, -50%)';
21012
+ default:
21013
+ return 'translate(-50%, 0)';
21014
+ }
21015
+ };
21016
+ var positionStyles = getPositionStyles();
20898
21017
  return /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
20899
21018
  ref: contentRef,
20900
21019
  id: contentId,
@@ -20911,7 +21030,13 @@
20911
21030
  maxWidth: maxWidth,
20912
21031
  // Combine intelligent position styles with user styles
20913
21032
  style: Object.assign({}, positionStyles, userStyle)
20914
- }, views == null ? void 0 : views.container, props), children);
21033
+ }, views == null ? void 0 : views.container, props), children, relation && (/*#__PURE__*/React__default.createElement("div", {
21034
+ style: {
21035
+ fontSize: '10px',
21036
+ opacity: 0.7,
21037
+ marginTop: '4px'
21038
+ }
21039
+ }, "Position: ", relation.position.vertical, "-", relation.position.horizontal, /*#__PURE__*/React__default.createElement("br", null), "More space: ", relation.space.vertical, "-", relation.space.horizontal)));
20915
21040
  };
20916
21041
 
20917
21042
  var _excluded$_ = ["children", "views", "openDelay", "closeDelay"];
@@ -24358,108 +24483,102 @@
24358
24483
  contentId,
24359
24484
  triggerId
24360
24485
  } = useTooltipContext();
24486
+ // Use useElementPosition for intelligent positioning
24487
+ var {
24488
+ ref: positionRef,
24489
+ relation
24490
+ } = appStudio.useElementPosition({
24491
+ trackChanges: true,
24492
+ trackOnHover: true,
24493
+ trackOnScroll: true,
24494
+ trackOnResize: true
24495
+ });
24361
24496
  var [optimalPosition, setOptimalPosition] = React.useState({
24362
24497
  x: 0,
24363
24498
  y: 0,
24364
24499
  placement: position
24365
24500
  });
24366
- // Calculate optimal position when the tooltip opens
24501
+ // Sync the position ref with the trigger ref for positioning calculations
24502
+ React.useEffect(() => {
24503
+ if (triggerRef != null && triggerRef.current && positionRef) {
24504
+ positionRef.current = triggerRef.current;
24505
+ }
24506
+ }, [triggerRef, positionRef, isOpen]);
24507
+ // Calculate optimal position using useElementPosition when the tooltip opens
24367
24508
  React.useEffect(() => {
24368
24509
  if (isOpen && contentRef != null && contentRef.current && triggerRef != null && triggerRef.current) {
24369
- var contentRect = contentRef.current.getBoundingClientRect();
24370
24510
  var triggerRect = triggerRef.current.getBoundingClientRect();
24371
- // Get content dimensions
24372
- var contentWidth = Math.max(contentRect.width || 120, 120);
24373
- var contentHeight = Math.max(contentRect.height || 32, 32);
24374
- // Get viewport dimensions
24375
- var viewportWidth = window.innerWidth;
24376
- var viewportHeight = window.innerHeight;
24377
- // Calculate available space on all sides from the trigger
24378
- var availableSpace = {
24379
- top: triggerRect.top,
24380
- right: viewportWidth - triggerRect.right,
24381
- bottom: viewportHeight - triggerRect.bottom,
24382
- left: triggerRect.left
24383
- };
24384
- // Determine optimal placement based on available space and preferred position
24385
- var placements = [{
24386
- placement: 'top',
24387
- space: availableSpace.top,
24388
- fits: availableSpace.top >= contentHeight + 16,
24389
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
24390
- y: triggerRect.top - contentHeight - 8
24391
- }, {
24392
- placement: 'bottom',
24393
- space: availableSpace.bottom,
24394
- fits: availableSpace.bottom >= contentHeight + 16,
24395
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
24396
- y: triggerRect.bottom + 8
24397
- }, {
24398
- placement: 'right',
24399
- space: availableSpace.right,
24400
- fits: availableSpace.right >= contentWidth + 16,
24401
- x: triggerRect.right + 8,
24402
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
24403
- }, {
24404
- placement: 'left',
24405
- space: availableSpace.left,
24406
- fits: availableSpace.left >= contentWidth + 16,
24407
- x: triggerRect.left - contentWidth - 8,
24408
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
24409
- }];
24410
- // First try the preferred position if it fits
24411
- var preferredPlacement = placements.find(p => p.placement === position && p.fits);
24412
- if (preferredPlacement) {
24413
- setOptimalPosition({
24414
- x: preferredPlacement.x,
24415
- y: preferredPlacement.y,
24416
- placement: preferredPlacement.placement
24417
- });
24418
- return;
24419
- }
24420
- // Otherwise, find the best fitting placement
24421
- var fittingPlacement = placements.find(p => p.fits);
24422
- if (fittingPlacement) {
24423
- setOptimalPosition({
24424
- x: fittingPlacement.x,
24425
- y: fittingPlacement.y,
24426
- placement: fittingPlacement.placement
24427
- });
24428
- return;
24429
- }
24430
- // If nothing fits, choose the placement with the most space
24431
- var bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
24432
- // Ensure the content stays within viewport bounds
24433
- var finalX = bestPlacement.x;
24434
- var finalY = bestPlacement.y;
24435
- if (finalX + contentWidth > viewportWidth) {
24436
- finalX = viewportWidth - contentWidth - 8;
24437
- }
24438
- if (finalX < 8) {
24439
- finalX = 8;
24440
- }
24441
- if (finalY + contentHeight > viewportHeight) {
24442
- finalY = viewportHeight - contentHeight - 8;
24511
+ var placement = position;
24512
+ // Use relation data to determine optimal placement
24513
+ if (relation) {
24514
+ // If preferred position doesn't have enough space, use the position with more space
24515
+ if (position === 'top' && relation.space.vertical === 'bottom') {
24516
+ placement = 'bottom';
24517
+ } else if (position === 'bottom' && relation.space.vertical === 'top') {
24518
+ placement = 'top';
24519
+ } else if (position === 'right' && relation.space.horizontal === 'left') {
24520
+ placement = 'left';
24521
+ } else if (position === 'left' && relation.space.horizontal === 'right') {
24522
+ placement = 'right';
24523
+ }
24443
24524
  }
24444
- if (finalY < 8) {
24445
- finalY = 8;
24525
+ // Calculate position based on optimal placement and alignment
24526
+ var x = 0;
24527
+ var y = 0;
24528
+ switch (placement) {
24529
+ case 'top':
24530
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 120 // Estimated content width
24531
+ : triggerRect.left + triggerRect.width / 2 - 60; // Half of estimated width
24532
+ y = triggerRect.top - 8;
24533
+ break;
24534
+ case 'bottom':
24535
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 120 : triggerRect.left + triggerRect.width / 2 - 60;
24536
+ y = triggerRect.bottom + 8;
24537
+ break;
24538
+ case 'right':
24539
+ x = triggerRect.right + 8;
24540
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 32 // Estimated content height
24541
+ : triggerRect.top + triggerRect.height / 2 - 16; // Half of estimated height
24542
+ break;
24543
+ case 'left':
24544
+ x = triggerRect.left - 8;
24545
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 32 : triggerRect.top + triggerRect.height / 2 - 16;
24546
+ break;
24446
24547
  }
24447
24548
  setOptimalPosition({
24448
- x: finalX,
24449
- y: finalY,
24450
- placement: bestPlacement.placement
24549
+ x,
24550
+ y,
24551
+ placement
24451
24552
  });
24452
24553
  }
24453
- }, [isOpen, position, align, triggerRef, contentRef]);
24554
+ }, [isOpen, position, align, triggerRef, contentRef, relation]);
24454
24555
  // Get arrow styles based on optimal placement
24455
24556
  var arrowStyles = showArrow ? getArrowStyles(optimalPosition.placement) : {};
24456
- // Create intelligent positioning styles
24457
- var positionStyles = {
24458
- position: 'fixed',
24459
- left: optimalPosition.x,
24460
- top: optimalPosition.y,
24461
- zIndex: 1000
24557
+ // Create intelligent positioning styles with transform for better placement
24558
+ var getPositionStyles = () => {
24559
+ var baseStyles = {
24560
+ position: 'fixed',
24561
+ left: optimalPosition.x,
24562
+ top: optimalPosition.y,
24563
+ zIndex: 1000
24564
+ };
24565
+ // Add transform based on placement for better positioning
24566
+ switch (optimalPosition.placement) {
24567
+ case 'top':
24568
+ return Object.assign({}, baseStyles, {
24569
+ transform: 'translateY(-100%)'
24570
+ });
24571
+ case 'left':
24572
+ return Object.assign({}, baseStyles, {
24573
+ transform: 'translateX(-100%)'
24574
+ });
24575
+ case 'bottom':
24576
+ case 'right':
24577
+ default:
24578
+ return baseStyles;
24579
+ }
24462
24580
  };
24581
+ var positionStyles = getPositionStyles();
24463
24582
  return /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
24464
24583
  position: "relative",
24465
24584
  display: "inline-block"
@@ -24471,7 +24590,13 @@
24471
24590
  borderRadius: 4,
24472
24591
  boxShadow: "0px 2px 8px rgba(0, 0, 0, 0.15)",
24473
24592
  style: positionStyles
24474
- }, TooltipSizes[size], TooltipVariants[variant], views == null ? void 0 : views.content), typeof content === 'string' ? (/*#__PURE__*/React__default.createElement(appStudio.Text, Object.assign({}, views == null ? void 0 : views.text), content)) : content, showArrow && /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({}, arrowStyles, views == null ? void 0 : views.arrow)))));
24593
+ }, TooltipSizes[size], TooltipVariants[variant], views == null ? void 0 : views.content), typeof content === 'string' ? (/*#__PURE__*/React__default.createElement(appStudio.Text, Object.assign({}, views == null ? void 0 : views.text), content)) : content, showArrow && /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({}, arrowStyles, views == null ? void 0 : views.arrow)), (/*#__PURE__*/React__default.createElement("div", {
24594
+ style: {
24595
+ fontSize: '8px',
24596
+ opacity: 0.7,
24597
+ marginTop: '2px'
24598
+ }
24599
+ }, "Placement: ", optimalPosition.placement, relation && (/*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("br", null), "Space: ", relation.space.vertical, "-", relation.space.horizontal)))))));
24475
24600
  };
24476
24601
 
24477
24602
  var _excluded$1b = ["content", "children", "position", "align", "size", "variant", "openDelay", "closeDelay", "showArrow", "defaultOpen", "isDisabled", "views"];