@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.
@@ -5742,6 +5742,24 @@ var PieChart = _ref => {
5742
5742
  var {
5743
5743
  getColor
5744
5744
  } = appStudio.useTheme();
5745
+ // Use useElementPosition for intelligent tooltip positioning
5746
+ var {
5747
+ ref: positionRef,
5748
+ relation
5749
+ } = appStudio.useElementPosition({
5750
+ trackChanges: true,
5751
+ trackOnHover: true,
5752
+ trackOnScroll: true,
5753
+ trackOnResize: true
5754
+ });
5755
+ // Create a separate ref for the SVG element
5756
+ var chartRef = React.useRef(null);
5757
+ // Sync the position ref with the chart ref for positioning calculations
5758
+ React.useEffect(() => {
5759
+ if (chartRef.current && positionRef) {
5760
+ positionRef.current = chartRef.current;
5761
+ }
5762
+ }, [chartRef, positionRef]);
5745
5763
  // Calculate chart dimensions
5746
5764
  var size = Math.min(width, height);
5747
5765
  var radius = size / 2 * 0.8;
@@ -5810,12 +5828,30 @@ var PieChart = _ref => {
5810
5828
  return result;
5811
5829
  }, [dataPoints, total, radius, centerX, centerY, donutRadius, animationProgress, isDonut]);
5812
5830
  return /*#__PURE__*/React__default.createElement("svg", {
5831
+ ref: chartRef,
5813
5832
  width: width,
5814
5833
  height: height
5815
5834
  }, slices.map((slice, index) => {
5816
5835
  var handleMouseEnter = e => {
5817
5836
  var tooltipContent = slice.label + ": " + slice.value + " (" + slice.percentage + ")";
5818
- showTooltip(e.clientX, e.clientY, tooltipContent);
5837
+ // Use intelligent positioning based on useElementPosition relation data
5838
+ var x = e.clientX;
5839
+ var y = e.clientY;
5840
+ if (relation && chartRef.current) {
5841
+ var chartRect = chartRef.current.getBoundingClientRect();
5842
+ // Adjust tooltip position based on available space
5843
+ if (relation.space.horizontal === 'left') {
5844
+ x = e.clientX - 100; // Offset tooltip to the left
5845
+ } else {
5846
+ x = e.clientX + 10; // Offset tooltip to the right
5847
+ }
5848
+ if (relation.space.vertical === 'top') {
5849
+ y = e.clientY - 30; // Offset tooltip above
5850
+ } else {
5851
+ y = e.clientY + 10; // Offset tooltip below
5852
+ }
5853
+ }
5854
+ showTooltip(x, y, tooltipContent);
5819
5855
  };
5820
5856
  var handleClick = () => {
5821
5857
  if (onSliceClick) {
@@ -13111,6 +13147,16 @@ var EditableInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13111
13147
  var [mentionStartPos, setMentionStartPos] = React.useState(-1);
13112
13148
  var [selectedMentionIndex, setSelectedMentionIndex] = React.useState(-1);
13113
13149
  var [filteredMentions, setFilteredMentions] = React.useState([]);
13150
+ // Use useElementPosition for intelligent dropdown positioning
13151
+ var {
13152
+ ref: positionRef,
13153
+ relation
13154
+ } = appStudio.useElementPosition({
13155
+ trackChanges: true,
13156
+ trackOnHover: true,
13157
+ trackOnScroll: true,
13158
+ trackOnResize: true
13159
+ });
13114
13160
  // Positioning state for dropdowns
13115
13161
  var [mentionPosition, setMentionPosition] = React.useState({
13116
13162
  x: 0,
@@ -13120,7 +13166,6 @@ var EditableInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13120
13166
  x: 0,
13121
13167
  y: 0
13122
13168
  });
13123
- // Note: Using custom positioning logic for better control over dropdown placement
13124
13169
  // Update the content of the editable div when the value prop changes
13125
13170
  React.useEffect(() => {
13126
13171
  var editableDiv = ref;
@@ -13172,7 +13217,13 @@ var EditableInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13172
13217
  setMentionStartPos(-1);
13173
13218
  setSelectedMentionIndex(-1);
13174
13219
  }, [mentionData, mentionTrigger]);
13175
- // Calculate optimal position for dropdowns
13220
+ // Sync the position ref with the container ref for positioning calculations
13221
+ React.useEffect(() => {
13222
+ if (containerRef.current && positionRef) {
13223
+ positionRef.current = containerRef.current;
13224
+ }
13225
+ }, [containerRef, positionRef]);
13226
+ // Calculate optimal position for dropdowns using useElementPosition
13176
13227
  var calculateDropdownPosition = React.useCallback(function (dropdownHeight) {
13177
13228
  if (dropdownHeight === void 0) {
13178
13229
  dropdownHeight = 200;
@@ -13182,22 +13233,26 @@ var EditableInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13182
13233
  y: 0
13183
13234
  };
13184
13235
  var containerRect = containerRef.current.getBoundingClientRect();
13236
+ // Use relation data for intelligent positioning if available
13237
+ if (relation) {
13238
+ var _useTopPlacement = relation.space.vertical === 'top';
13239
+ return {
13240
+ x: containerRect.left,
13241
+ y: _useTopPlacement ? containerRect.top - dropdownHeight - 8 : containerRect.bottom + 8
13242
+ };
13243
+ }
13244
+ // Fallback to manual calculation if relation data is not available
13185
13245
  var viewportHeight = window.innerHeight;
13186
- var viewportWidth = window.innerWidth;
13187
- // Calculate available space
13188
13246
  var availableSpace = {
13189
13247
  top: containerRect.top,
13190
- bottom: viewportHeight - containerRect.bottom,
13191
- left: containerRect.left,
13192
- right: viewportWidth - containerRect.right
13248
+ bottom: viewportHeight - containerRect.bottom
13193
13249
  };
13194
- // Prefer bottom placement, but use top if not enough space
13195
13250
  var useTopPlacement = availableSpace.bottom < dropdownHeight + 8 && availableSpace.top > availableSpace.bottom;
13196
13251
  return {
13197
13252
  x: containerRect.left,
13198
13253
  y: useTopPlacement ? containerRect.top - dropdownHeight - 8 : containerRect.bottom + 8
13199
13254
  };
13200
- }, []);
13255
+ }, [relation]);
13201
13256
  // Handle focus events
13202
13257
  var handleFocus = React.useCallback(() => {
13203
13258
  setIsFocused(true);
@@ -13403,7 +13458,7 @@ var EditableInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13403
13458
  opacity: 0.7,
13404
13459
  padding: '4px'
13405
13460
  }
13406
- }, "Mentions (Trigger: ", mentionTrigger, ")"))))), showSuggestions && suggestions.length > 0 && !showMentions && isFocused && !value && (/*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
13461
+ }, "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({
13407
13462
  position: "fixed",
13408
13463
  left: suggestionPosition.x,
13409
13464
  top: suggestionPosition.y,
@@ -13449,7 +13504,7 @@ var EditableInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
13449
13504
  opacity: 0.7,
13450
13505
  padding: '4px'
13451
13506
  }
13452
- }, "Suggestions (Focus-triggered)"))))));
13507
+ }, "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))))))));
13453
13508
  });
13454
13509
  EditableInput.displayName = 'EditableInput';
13455
13510
 
@@ -17036,8 +17091,78 @@ var useToggleState = defaultToggled => {
17036
17091
  };
17037
17092
  };
17038
17093
 
17039
- var _excluded$T = ["children", "shape", "variant", "isHovered", "setIsHovered", "isDisabled", "isToggle", "setIsToggled", "onToggle", "views"];
17094
+ var ToggleShapes = {
17095
+ sharp: 0,
17096
+ rounded: 4,
17097
+ pillShaped: 24
17098
+ };
17099
+ /**
17100
+ * Generate toggle variants with proper color combinations based on main color and contrast
17101
+ * Similar to Button's getButtonVariants function
17102
+ */
17103
+ var getToggleVariants = (color, isLight) => ({
17104
+ outline: {
17105
+ backgroundColor: 'transparent',
17106
+ color: color,
17107
+ borderWidth: 1,
17108
+ borderStyle: 'solid',
17109
+ borderColor: color,
17110
+ _hover: {
17111
+ backgroundColor: color,
17112
+ color: isLight ? 'color.black' : 'color.white',
17113
+ transform: 'translateY(-1px)'
17114
+ },
17115
+ _active: {
17116
+ backgroundColor: color,
17117
+ color: isLight ? 'color.black' : 'color.white',
17118
+ transform: 'translateY(0)'
17119
+ },
17120
+ transition: 'all 0.2s ease'
17121
+ },
17122
+ ghost: {
17123
+ backgroundColor: 'transparent',
17124
+ color: color,
17125
+ borderWidth: 0,
17126
+ borderStyle: 'none',
17127
+ borderColor: 'transparent',
17128
+ _hover: {
17129
+ backgroundColor: color,
17130
+ color: isLight ? 'color.black' : 'color.white',
17131
+ transform: 'translateY(-1px)'
17132
+ },
17133
+ _active: {
17134
+ backgroundColor: color,
17135
+ color: isLight ? 'color.black' : 'color.white',
17136
+ transform: 'translateY(0)'
17137
+ },
17138
+ transition: 'all 0.2s ease'
17139
+ },
17140
+ link: {
17141
+ backgroundColor: 'transparent',
17142
+ color: color,
17143
+ borderWidth: 1,
17144
+ borderStyle: 'solid',
17145
+ borderColor: 'transparent',
17146
+ textDecoration: 'underline',
17147
+ textUnderlineOffset: '1px',
17148
+ textDecorationThickness: '1px',
17149
+ _hover: {
17150
+ borderColor: color,
17151
+ textDecorationThickness: '2px',
17152
+ transform: 'translateY(-1px)'
17153
+ },
17154
+ _active: {
17155
+ borderColor: color,
17156
+ textDecorationThickness: '2px',
17157
+ transform: 'translateY(0)'
17158
+ },
17159
+ transition: 'all 0.2s ease'
17160
+ }
17161
+ });
17162
+
17163
+ var _excluded$T = ["children", "shape", "variant", "isHovered", "setIsHovered", "isDisabled", "isToggle", "setIsToggled", "onToggle", "views", "backgroundColor", "color", "themeMode"];
17040
17164
  var ToggleView = _ref => {
17165
+ var _ref2;
17041
17166
  var {
17042
17167
  children,
17043
17168
  shape = 'rounded',
@@ -17048,25 +17173,30 @@ var ToggleView = _ref => {
17048
17173
  isToggle,
17049
17174
  setIsToggled,
17050
17175
  onToggle,
17051
- views
17176
+ views,
17177
+ backgroundColor,
17178
+ // primary candidate for main color
17179
+ color,
17180
+ // 2nd candidate for main color
17181
+ themeMode: elementMode
17052
17182
  } = _ref,
17053
17183
  props = _objectWithoutPropertiesLoose(_ref, _excluded$T);
17054
- var toggleColor = !isDisabled ? 'color.trueGray.400' : 'theme.disabled';
17184
+ /* theme helpers */
17185
+ var {
17186
+ getColor,
17187
+ themeMode
17188
+ } = appStudio.useTheme();
17189
+ var mode = elementMode != null ? elementMode : themeMode;
17190
+ /* MAIN COLOR – determines the entire palette */
17191
+ var mainColorKey = (_ref2 = backgroundColor != null ? backgroundColor : color) != null ? _ref2 : 'theme.primary';
17192
+ var mainTone = getColor(isDisabled ? 'theme.disabled' : mainColorKey, {
17193
+ themeMode: mode
17194
+ });
17195
+ var tone = contrast(mainTone);
17196
+ /* variant palette */
17197
+ var palette = React.useMemo(() => getToggleVariants(mainTone, tone === 'light'), [mainTone, tone]);
17198
+ var base = palette[variant];
17055
17199
  var isActive = !!(isToggle || isHovered);
17056
- var toggleVariants = {
17057
- outline: {
17058
- borderWidth: 1,
17059
- borderStyle: 'solid',
17060
- borderColor: 'color.trueGray.400'
17061
- },
17062
- link: {
17063
- borderWidth: 1,
17064
- borderStyle: 'solid',
17065
- borderColor: isActive ? toggleColor : 'transparent',
17066
- textDecoration: 'underline'
17067
- },
17068
- ghost: {}
17069
- };
17070
17200
  var handleHover = () => setIsHovered(!isHovered);
17071
17201
  var handleToggle = () => {
17072
17202
  if (!isDisabled) {
@@ -17083,14 +17213,15 @@ var ToggleView = _ref => {
17083
17213
  role: "Toggle",
17084
17214
  padding: shape === 'pillShaped' ? '10px 12px' : '8px',
17085
17215
  width: "fit-content",
17086
- color: isActive ? 'color.white' : toggleColor,
17087
- backgroundColor: isActive ? toggleColor : 'transparent',
17088
- onMouseEnter: handleHover,
17089
- onMouseLeave: () => setIsHovered(false),
17090
17216
  cursor: isDisabled ? 'not-allowed' : 'pointer',
17091
- borderRadius: shape === 'pillShaped' ? '50px' : '8px',
17092
- onClick: handleToggle
17093
- }, toggleVariants[variant], props, views == null ? void 0 : views.container), children);
17217
+ borderRadius: ToggleShapes[shape],
17218
+ onClick: handleToggle,
17219
+ onMouseEnter: handleHover,
17220
+ onMouseLeave: () => setIsHovered(false)
17221
+ }, base, isActive && {
17222
+ backgroundColor: mainTone,
17223
+ color: tone === 'light' ? 'color.black' : 'color.white'
17224
+ }, props, views == null ? void 0 : views.container), children);
17094
17225
  };
17095
17226
 
17096
17227
  var _excluded$U = ["children", "shape", "variant", "isDisabled", "isToggled", "onToggle"];
@@ -17515,109 +17646,103 @@ var DropdownMenuContent = _ref3 => {
17515
17646
  triggerRef
17516
17647
  } = useDropdownMenuContext();
17517
17648
  var contentRef = React.useRef(null);
17649
+ // Use useElementPosition for intelligent positioning
17650
+ var {
17651
+ ref: positionRef,
17652
+ relation
17653
+ } = appStudio.useElementPosition({
17654
+ trackChanges: true,
17655
+ trackOnHover: true,
17656
+ trackOnScroll: true,
17657
+ trackOnResize: true
17658
+ });
17518
17659
  var [optimalPosition, setOptimalPosition] = React.useState({
17519
17660
  x: 0,
17520
17661
  y: 0,
17521
17662
  placement: side
17522
17663
  });
17523
- // Calculate optimal position when the dropdown opens
17664
+ // Sync the position ref with the trigger ref for positioning calculations
17665
+ React.useEffect(() => {
17666
+ if (triggerRef.current && positionRef) {
17667
+ positionRef.current = triggerRef.current;
17668
+ }
17669
+ }, [triggerRef, positionRef, isOpen]);
17670
+ // Calculate optimal position using useElementPosition when the dropdown opens
17524
17671
  React.useEffect(() => {
17525
17672
  if (isOpen && contentRef.current && triggerRef.current) {
17526
- var contentRect = contentRef.current.getBoundingClientRect();
17527
17673
  var triggerRect = triggerRef.current.getBoundingClientRect();
17528
- // Get content dimensions
17529
- var contentWidth = Math.max(contentRect.width || 180, 180);
17530
- var contentHeight = Math.max(contentRect.height || 100, 100);
17531
- // Get viewport dimensions
17532
- var viewportWidth = window.innerWidth;
17533
- var viewportHeight = window.innerHeight;
17534
- // Calculate available space on all sides from the trigger
17535
- var availableSpace = {
17536
- top: triggerRect.top,
17537
- right: viewportWidth - triggerRect.right,
17538
- bottom: viewportHeight - triggerRect.bottom,
17539
- left: triggerRect.left
17540
- };
17541
- // Determine optimal placement based on available space and preferred side
17542
- var placements = [{
17543
- placement: 'bottom',
17544
- space: availableSpace.bottom,
17545
- fits: availableSpace.bottom >= contentHeight + 8,
17546
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
17547
- y: triggerRect.bottom + 8
17548
- }, {
17549
- placement: 'top',
17550
- space: availableSpace.top,
17551
- fits: availableSpace.top >= contentHeight + 8,
17552
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
17553
- y: triggerRect.top - contentHeight - 8
17554
- }, {
17555
- placement: 'right',
17556
- space: availableSpace.right,
17557
- fits: availableSpace.right >= contentWidth + 8,
17558
- x: triggerRect.right + 8,
17559
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
17560
- }, {
17561
- placement: 'left',
17562
- space: availableSpace.left,
17563
- fits: availableSpace.left >= contentWidth + 8,
17564
- x: triggerRect.left - contentWidth - 8,
17565
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
17566
- }];
17567
- // First try the preferred side if it fits
17568
- var preferredPlacement = placements.find(p => p.placement === side && p.fits);
17569
- if (preferredPlacement) {
17570
- setOptimalPosition({
17571
- x: preferredPlacement.x,
17572
- y: preferredPlacement.y,
17573
- placement: preferredPlacement.placement
17574
- });
17575
- return;
17576
- }
17577
- // Otherwise, find the best fitting placement
17578
- var fittingPlacement = placements.find(p => p.fits);
17579
- if (fittingPlacement) {
17580
- setOptimalPosition({
17581
- x: fittingPlacement.x,
17582
- y: fittingPlacement.y,
17583
- placement: fittingPlacement.placement
17584
- });
17585
- return;
17586
- }
17587
- // If nothing fits, choose the placement with the most space
17588
- var bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
17589
- // Ensure the content stays within viewport bounds
17590
- var finalX = bestPlacement.x;
17591
- var finalY = bestPlacement.y;
17592
- if (finalX + contentWidth > viewportWidth) {
17593
- finalX = viewportWidth - contentWidth - 8;
17594
- }
17595
- if (finalX < 8) {
17596
- finalX = 8;
17674
+ var placement = side;
17675
+ // Use relation data to determine optimal placement
17676
+ if (relation) {
17677
+ // If preferred side doesn't have enough space, use the side with more space
17678
+ if (side === 'bottom' && relation.space.vertical === 'top') {
17679
+ placement = 'top';
17680
+ } else if (side === 'top' && relation.space.vertical === 'bottom') {
17681
+ placement = 'bottom';
17682
+ } else if (side === 'right' && relation.space.horizontal === 'left') {
17683
+ placement = 'left';
17684
+ } else if (side === 'left' && relation.space.horizontal === 'right') {
17685
+ placement = 'right';
17686
+ }
17597
17687
  }
17598
- if (finalY + contentHeight > viewportHeight) {
17599
- finalY = viewportHeight - contentHeight - 8;
17600
- }
17601
- if (finalY < 8) {
17602
- finalY = 8;
17688
+ // Calculate position based on optimal placement and alignment
17689
+ var x = 0;
17690
+ var y = 0;
17691
+ switch (placement) {
17692
+ case 'bottom':
17693
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 180 // Estimated content width
17694
+ : triggerRect.left + triggerRect.width / 2 - 90; // Half of estimated width
17695
+ y = triggerRect.bottom + 8;
17696
+ break;
17697
+ case 'top':
17698
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 180 : triggerRect.left + triggerRect.width / 2 - 90;
17699
+ y = triggerRect.top - 8; // Will be adjusted with transform
17700
+ break;
17701
+ case 'right':
17702
+ x = triggerRect.right + 8;
17703
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 100 // Estimated content height
17704
+ : triggerRect.top + triggerRect.height / 2 - 50; // Half of estimated height
17705
+ break;
17706
+ case 'left':
17707
+ x = triggerRect.left - 8; // Will be adjusted with transform
17708
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 100 : triggerRect.top + triggerRect.height / 2 - 50;
17709
+ break;
17603
17710
  }
17604
17711
  setOptimalPosition({
17605
- x: finalX,
17606
- y: finalY,
17607
- placement: bestPlacement.placement
17712
+ x,
17713
+ y,
17714
+ placement
17608
17715
  });
17609
17716
  }
17610
- }, [isOpen, side, align, triggerRef]);
17717
+ }, [isOpen, side, align, triggerRef, relation]);
17611
17718
  if (!isOpen) {
17612
17719
  return null;
17613
17720
  }
17614
- // Create intelligent positioning styles
17615
- var positionStyles = {
17616
- position: 'fixed',
17617
- left: optimalPosition.x,
17618
- top: optimalPosition.y,
17619
- zIndex: 1000
17721
+ // Create intelligent positioning styles with transform for better placement
17722
+ var getPositionStyles = () => {
17723
+ var baseStyles = {
17724
+ position: 'fixed',
17725
+ left: optimalPosition.x,
17726
+ top: optimalPosition.y,
17727
+ zIndex: 1000
17728
+ };
17729
+ // Add transform based on placement for better positioning
17730
+ switch (optimalPosition.placement) {
17731
+ case 'top':
17732
+ return Object.assign({}, baseStyles, {
17733
+ transform: 'translateY(-100%)'
17734
+ });
17735
+ case 'left':
17736
+ return Object.assign({}, baseStyles, {
17737
+ transform: 'translateX(-100%)'
17738
+ });
17739
+ case 'bottom':
17740
+ case 'right':
17741
+ default:
17742
+ return baseStyles;
17743
+ }
17620
17744
  };
17745
+ var positionStyles = getPositionStyles();
17621
17746
  return /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
17622
17747
  ref: contentRef,
17623
17748
  id: "dropdown-menu",
@@ -17637,7 +17762,13 @@ var DropdownMenuContent = _ref3 => {
17637
17762
  item: item,
17638
17763
  views: views
17639
17764
  });
17640
- }));
17765
+ }), (/*#__PURE__*/React__default.createElement("div", {
17766
+ style: {
17767
+ fontSize: '10px',
17768
+ opacity: 0.7,
17769
+ padding: '4px'
17770
+ }
17771
+ }, "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)))));
17641
17772
  };
17642
17773
  // DropdownMenu Item component
17643
17774
  var DropdownMenuItem = _ref4 => {
@@ -20821,111 +20952,99 @@ var HoverCardContent = _ref3 => {
20821
20952
  contentId,
20822
20953
  triggerId
20823
20954
  } = useHoverCardContext();
20824
- var [optimalPosition, setOptimalPosition] = React.useState({
20825
- x: 0,
20826
- y: 0,
20827
- placement: side
20955
+ // Use useElementPosition for intelligent positioning
20956
+ var {
20957
+ ref: positionRef,
20958
+ relation
20959
+ } = appStudio.useElementPosition({
20960
+ trackChanges: true,
20961
+ trackOnHover: true,
20962
+ trackOnScroll: true,
20963
+ trackOnResize: true
20828
20964
  });
20829
- // Calculate optimal position when the card opens or content dimensions change
20965
+ // Sync the position ref with the trigger ref for positioning calculations
20830
20966
  React.useEffect(() => {
20831
- if (isOpen && contentRef != null && contentRef.current && triggerRef != null && triggerRef.current) {
20832
- var contentRect = contentRef.current.getBoundingClientRect();
20833
- var triggerRect = triggerRef.current.getBoundingClientRect();
20834
- // Get content dimensions
20835
- var contentWidth = Math.max(contentRect.width || 200, 200);
20836
- var contentHeight = Math.max(contentRect.height || 100, 100);
20837
- // Get viewport dimensions
20838
- var viewportWidth = window.innerWidth;
20839
- var viewportHeight = window.innerHeight;
20840
- // Calculate available space on all sides from the trigger
20841
- var availableSpace = {
20842
- top: triggerRect.top,
20843
- right: viewportWidth - triggerRect.right,
20844
- bottom: viewportHeight - triggerRect.bottom,
20845
- left: triggerRect.left
20846
- };
20847
- // Determine optimal placement based on available space and preferred side
20848
- var placements = [{
20849
- placement: 'bottom',
20850
- space: availableSpace.bottom,
20851
- fits: availableSpace.bottom >= contentHeight + sideOffset,
20852
- x: triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
20853
- y: triggerRect.bottom + sideOffset
20854
- }, {
20855
- placement: 'top',
20856
- space: availableSpace.top,
20857
- fits: availableSpace.top >= contentHeight + sideOffset,
20858
- x: triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
20859
- y: triggerRect.top - contentHeight - sideOffset
20860
- }, {
20861
- placement: 'right',
20862
- space: availableSpace.right,
20863
- fits: availableSpace.right >= contentWidth + sideOffset,
20864
- x: triggerRect.right + sideOffset,
20865
- y: triggerRect.top + triggerRect.height / 2 - contentHeight / 2
20866
- }, {
20867
- placement: 'left',
20868
- space: availableSpace.left,
20869
- fits: availableSpace.left >= contentWidth + sideOffset,
20870
- x: triggerRect.left - contentWidth - sideOffset,
20871
- y: triggerRect.top + triggerRect.height / 2 - contentHeight / 2
20872
- }];
20873
- // First try the preferred side if it fits
20874
- var preferredPlacement = placements.find(p => p.placement === side && p.fits);
20875
- if (preferredPlacement) {
20876
- setOptimalPosition({
20877
- x: preferredPlacement.x,
20878
- y: preferredPlacement.y,
20879
- placement: preferredPlacement.placement
20880
- });
20881
- return;
20967
+ if (triggerRef != null && triggerRef.current && (positionRef == null ? void 0 : positionRef.current) !== triggerRef.current) {
20968
+ // Update the position tracking to use the trigger element
20969
+ if (positionRef) {
20970
+ positionRef.current = triggerRef.current;
20882
20971
  }
20883
- // Otherwise, find the best fitting placement
20884
- var fittingPlacement = placements.find(p => p.fits);
20885
- if (fittingPlacement) {
20886
- setOptimalPosition({
20887
- x: fittingPlacement.x,
20888
- y: fittingPlacement.y,
20889
- placement: fittingPlacement.placement
20890
- });
20891
- return;
20892
- }
20893
- // If nothing fits, choose the placement with the most space
20894
- var bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
20895
- // Ensure the content stays within viewport bounds
20896
- var finalX = bestPlacement.x;
20897
- var finalY = bestPlacement.y;
20898
- if (finalX + contentWidth > viewportWidth) {
20899
- finalX = viewportWidth - contentWidth - 8;
20900
- }
20901
- if (finalX < 8) {
20902
- finalX = 8;
20903
- }
20904
- if (finalY + contentHeight > viewportHeight) {
20905
- finalY = viewportHeight - contentHeight - 8;
20906
- }
20907
- if (finalY < 8) {
20908
- finalY = 8;
20909
- }
20910
- setOptimalPosition({
20911
- x: finalX,
20912
- y: finalY,
20913
- placement: bestPlacement.placement
20914
- });
20915
20972
  }
20916
- }, [isOpen, side, sideOffset, contentRef, triggerRef]);
20973
+ }, [triggerRef, positionRef, isOpen]);
20917
20974
  var handleMouseEnter = () => cancelCloseTimer(); // Keep card open if mouse enters content
20918
20975
  var handleMouseLeave = () => closeCard();
20919
20976
  if (!isOpen) {
20920
20977
  return null; // Don't render content if not open
20921
20978
  }
20922
- // Create intelligent positioning styles
20923
- var positionStyles = {
20924
- position: 'fixed',
20925
- left: optimalPosition.x,
20926
- top: optimalPosition.y,
20927
- zIndex: 1000
20979
+ // Create intelligent positioning styles based on useElementPosition relation data
20980
+ var getPositionStyles = () => {
20981
+ if (!relation || !(triggerRef != null && triggerRef.current)) {
20982
+ // Fallback positioning if relation data is not available
20983
+ return {
20984
+ position: 'absolute',
20985
+ top: 0,
20986
+ left: 0,
20987
+ zIndex: 1000
20988
+ };
20989
+ }
20990
+ var triggerRect = triggerRef.current.getBoundingClientRect();
20991
+ var placement = side;
20992
+ // Use relation data to determine optimal placement
20993
+ // If preferred side doesn't have enough space, use the side with more space
20994
+ if (side === 'bottom' && relation.space.vertical === 'top') {
20995
+ placement = 'top';
20996
+ } else if (side === 'top' && relation.space.vertical === 'bottom') {
20997
+ placement = 'bottom';
20998
+ } else if (side === 'right' && relation.space.horizontal === 'left') {
20999
+ placement = 'left';
21000
+ } else if (side === 'left' && relation.space.horizontal === 'right') {
21001
+ placement = 'right';
21002
+ }
21003
+ // Calculate position based on optimal placement
21004
+ var x = 0;
21005
+ var y = 0;
21006
+ switch (placement) {
21007
+ case 'bottom':
21008
+ x = triggerRect.left + triggerRect.width / 2;
21009
+ y = triggerRect.bottom + sideOffset;
21010
+ break;
21011
+ case 'top':
21012
+ x = triggerRect.left + triggerRect.width / 2;
21013
+ y = triggerRect.top - sideOffset;
21014
+ break;
21015
+ case 'right':
21016
+ x = triggerRect.right + sideOffset;
21017
+ y = triggerRect.top + triggerRect.height / 2;
21018
+ break;
21019
+ case 'left':
21020
+ x = triggerRect.left - sideOffset;
21021
+ y = triggerRect.top + triggerRect.height / 2;
21022
+ break;
21023
+ }
21024
+ return {
21025
+ position: 'fixed',
21026
+ left: x,
21027
+ top: y,
21028
+ zIndex: 1000,
21029
+ transform: getTransformOrigin(placement)
21030
+ };
20928
21031
  };
21032
+ // Helper function to set transform origin for better positioning
21033
+ var getTransformOrigin = placement => {
21034
+ switch (placement) {
21035
+ case 'bottom':
21036
+ return 'translate(-50%, 0)';
21037
+ case 'top':
21038
+ return 'translate(-50%, -100%)';
21039
+ case 'right':
21040
+ return 'translate(0, -50%)';
21041
+ case 'left':
21042
+ return 'translate(-100%, -50%)';
21043
+ default:
21044
+ return 'translate(-50%, 0)';
21045
+ }
21046
+ };
21047
+ var positionStyles = getPositionStyles();
20929
21048
  return /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
20930
21049
  ref: contentRef,
20931
21050
  id: contentId,
@@ -20942,7 +21061,13 @@ var HoverCardContent = _ref3 => {
20942
21061
  maxWidth: maxWidth,
20943
21062
  // Combine intelligent position styles with user styles
20944
21063
  style: Object.assign({}, positionStyles, userStyle)
20945
- }, views == null ? void 0 : views.container, props), children);
21064
+ }, views == null ? void 0 : views.container, props), children, relation && (/*#__PURE__*/React__default.createElement("div", {
21065
+ style: {
21066
+ fontSize: '10px',
21067
+ opacity: 0.7,
21068
+ marginTop: '4px'
21069
+ }
21070
+ }, "Position: ", relation.position.vertical, "-", relation.position.horizontal, /*#__PURE__*/React__default.createElement("br", null), "More space: ", relation.space.vertical, "-", relation.space.horizontal)));
20946
21071
  };
20947
21072
 
20948
21073
  var _excluded$_ = ["children", "views", "openDelay", "closeDelay"];
@@ -24389,108 +24514,102 @@ var TooltipView = _ref4 => {
24389
24514
  contentId,
24390
24515
  triggerId
24391
24516
  } = useTooltipContext();
24517
+ // Use useElementPosition for intelligent positioning
24518
+ var {
24519
+ ref: positionRef,
24520
+ relation
24521
+ } = appStudio.useElementPosition({
24522
+ trackChanges: true,
24523
+ trackOnHover: true,
24524
+ trackOnScroll: true,
24525
+ trackOnResize: true
24526
+ });
24392
24527
  var [optimalPosition, setOptimalPosition] = React.useState({
24393
24528
  x: 0,
24394
24529
  y: 0,
24395
24530
  placement: position
24396
24531
  });
24397
- // Calculate optimal position when the tooltip opens
24532
+ // Sync the position ref with the trigger ref for positioning calculations
24533
+ React.useEffect(() => {
24534
+ if (triggerRef != null && triggerRef.current && positionRef) {
24535
+ positionRef.current = triggerRef.current;
24536
+ }
24537
+ }, [triggerRef, positionRef, isOpen]);
24538
+ // Calculate optimal position using useElementPosition when the tooltip opens
24398
24539
  React.useEffect(() => {
24399
24540
  if (isOpen && contentRef != null && contentRef.current && triggerRef != null && triggerRef.current) {
24400
- var contentRect = contentRef.current.getBoundingClientRect();
24401
24541
  var triggerRect = triggerRef.current.getBoundingClientRect();
24402
- // Get content dimensions
24403
- var contentWidth = Math.max(contentRect.width || 120, 120);
24404
- var contentHeight = Math.max(contentRect.height || 32, 32);
24405
- // Get viewport dimensions
24406
- var viewportWidth = window.innerWidth;
24407
- var viewportHeight = window.innerHeight;
24408
- // Calculate available space on all sides from the trigger
24409
- var availableSpace = {
24410
- top: triggerRect.top,
24411
- right: viewportWidth - triggerRect.right,
24412
- bottom: viewportHeight - triggerRect.bottom,
24413
- left: triggerRect.left
24414
- };
24415
- // Determine optimal placement based on available space and preferred position
24416
- var placements = [{
24417
- placement: 'top',
24418
- space: availableSpace.top,
24419
- fits: availableSpace.top >= contentHeight + 16,
24420
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
24421
- y: triggerRect.top - contentHeight - 8
24422
- }, {
24423
- placement: 'bottom',
24424
- space: availableSpace.bottom,
24425
- fits: availableSpace.bottom >= contentHeight + 16,
24426
- x: align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - contentWidth : triggerRect.left + triggerRect.width / 2 - contentWidth / 2,
24427
- y: triggerRect.bottom + 8
24428
- }, {
24429
- placement: 'right',
24430
- space: availableSpace.right,
24431
- fits: availableSpace.right >= contentWidth + 16,
24432
- x: triggerRect.right + 8,
24433
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
24434
- }, {
24435
- placement: 'left',
24436
- space: availableSpace.left,
24437
- fits: availableSpace.left >= contentWidth + 16,
24438
- x: triggerRect.left - contentWidth - 8,
24439
- y: align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - contentHeight : triggerRect.top + triggerRect.height / 2 - contentHeight / 2
24440
- }];
24441
- // First try the preferred position if it fits
24442
- var preferredPlacement = placements.find(p => p.placement === position && p.fits);
24443
- if (preferredPlacement) {
24444
- setOptimalPosition({
24445
- x: preferredPlacement.x,
24446
- y: preferredPlacement.y,
24447
- placement: preferredPlacement.placement
24448
- });
24449
- return;
24450
- }
24451
- // Otherwise, find the best fitting placement
24452
- var fittingPlacement = placements.find(p => p.fits);
24453
- if (fittingPlacement) {
24454
- setOptimalPosition({
24455
- x: fittingPlacement.x,
24456
- y: fittingPlacement.y,
24457
- placement: fittingPlacement.placement
24458
- });
24459
- return;
24460
- }
24461
- // If nothing fits, choose the placement with the most space
24462
- var bestPlacement = placements.reduce((best, current) => current.space > best.space ? current : best);
24463
- // Ensure the content stays within viewport bounds
24464
- var finalX = bestPlacement.x;
24465
- var finalY = bestPlacement.y;
24466
- if (finalX + contentWidth > viewportWidth) {
24467
- finalX = viewportWidth - contentWidth - 8;
24468
- }
24469
- if (finalX < 8) {
24470
- finalX = 8;
24471
- }
24472
- if (finalY + contentHeight > viewportHeight) {
24473
- finalY = viewportHeight - contentHeight - 8;
24542
+ var placement = position;
24543
+ // Use relation data to determine optimal placement
24544
+ if (relation) {
24545
+ // If preferred position doesn't have enough space, use the position with more space
24546
+ if (position === 'top' && relation.space.vertical === 'bottom') {
24547
+ placement = 'bottom';
24548
+ } else if (position === 'bottom' && relation.space.vertical === 'top') {
24549
+ placement = 'top';
24550
+ } else if (position === 'right' && relation.space.horizontal === 'left') {
24551
+ placement = 'left';
24552
+ } else if (position === 'left' && relation.space.horizontal === 'right') {
24553
+ placement = 'right';
24554
+ }
24474
24555
  }
24475
- if (finalY < 8) {
24476
- finalY = 8;
24556
+ // Calculate position based on optimal placement and alignment
24557
+ var x = 0;
24558
+ var y = 0;
24559
+ switch (placement) {
24560
+ case 'top':
24561
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 120 // Estimated content width
24562
+ : triggerRect.left + triggerRect.width / 2 - 60; // Half of estimated width
24563
+ y = triggerRect.top - 8;
24564
+ break;
24565
+ case 'bottom':
24566
+ x = align === 'start' ? triggerRect.left : align === 'end' ? triggerRect.right - 120 : triggerRect.left + triggerRect.width / 2 - 60;
24567
+ y = triggerRect.bottom + 8;
24568
+ break;
24569
+ case 'right':
24570
+ x = triggerRect.right + 8;
24571
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 32 // Estimated content height
24572
+ : triggerRect.top + triggerRect.height / 2 - 16; // Half of estimated height
24573
+ break;
24574
+ case 'left':
24575
+ x = triggerRect.left - 8;
24576
+ y = align === 'start' ? triggerRect.top : align === 'end' ? triggerRect.bottom - 32 : triggerRect.top + triggerRect.height / 2 - 16;
24577
+ break;
24477
24578
  }
24478
24579
  setOptimalPosition({
24479
- x: finalX,
24480
- y: finalY,
24481
- placement: bestPlacement.placement
24580
+ x,
24581
+ y,
24582
+ placement
24482
24583
  });
24483
24584
  }
24484
- }, [isOpen, position, align, triggerRef, contentRef]);
24585
+ }, [isOpen, position, align, triggerRef, contentRef, relation]);
24485
24586
  // Get arrow styles based on optimal placement
24486
24587
  var arrowStyles = showArrow ? getArrowStyles(optimalPosition.placement) : {};
24487
- // Create intelligent positioning styles
24488
- var positionStyles = {
24489
- position: 'fixed',
24490
- left: optimalPosition.x,
24491
- top: optimalPosition.y,
24492
- zIndex: 1000
24588
+ // Create intelligent positioning styles with transform for better placement
24589
+ var getPositionStyles = () => {
24590
+ var baseStyles = {
24591
+ position: 'fixed',
24592
+ left: optimalPosition.x,
24593
+ top: optimalPosition.y,
24594
+ zIndex: 1000
24595
+ };
24596
+ // Add transform based on placement for better positioning
24597
+ switch (optimalPosition.placement) {
24598
+ case 'top':
24599
+ return Object.assign({}, baseStyles, {
24600
+ transform: 'translateY(-100%)'
24601
+ });
24602
+ case 'left':
24603
+ return Object.assign({}, baseStyles, {
24604
+ transform: 'translateX(-100%)'
24605
+ });
24606
+ case 'bottom':
24607
+ case 'right':
24608
+ default:
24609
+ return baseStyles;
24610
+ }
24493
24611
  };
24612
+ var positionStyles = getPositionStyles();
24494
24613
  return /*#__PURE__*/React__default.createElement(appStudio.View, Object.assign({
24495
24614
  position: "relative",
24496
24615
  display: "inline-block"
@@ -24502,7 +24621,13 @@ var TooltipView = _ref4 => {
24502
24621
  borderRadius: 4,
24503
24622
  boxShadow: "0px 2px 8px rgba(0, 0, 0, 0.15)",
24504
24623
  style: positionStyles
24505
- }, 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)))));
24624
+ }, 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", {
24625
+ style: {
24626
+ fontSize: '8px',
24627
+ opacity: 0.7,
24628
+ marginTop: '2px'
24629
+ }
24630
+ }, "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)))))));
24506
24631
  };
24507
24632
 
24508
24633
  var _excluded$1b = ["content", "children", "position", "align", "size", "variant", "openDelay", "closeDelay", "showArrow", "defaultOpen", "isDisabled", "views"];