raise-common-lib 0.0.220 → 0.0.222-beta

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.
@@ -22884,22 +22884,91 @@ class SmartPopupComponent {
22884
22884
  if (this.opened === "yes" &&
22885
22885
  previousTrigger !== this.currentTriggerElement &&
22886
22886
  this.currentTriggerElement) {
22887
- // 使用新的打开方式更新位置
22888
- this.positioning = "yes";
22889
- this.ref.markForCheck();
22890
- requestAnimationFrame((/**
22891
- * @return {?}
22892
- */
22893
- () => {
22887
+ // 如果提供了固定尺寸,直接重新计算位置(避免位置跳动)
22888
+ if (this._width !== null && this._height !== null) {
22889
+ // 先隐藏弹窗(positioning = yes)
22890
+ this.positioning = "yes";
22891
+ this.ref.markForCheck();
22892
+ this.ref.detectChanges(); // 立即应用隐藏状态
22894
22893
  requestAnimationFrame((/**
22895
22894
  * @return {?}
22896
22895
  */
22897
22896
  () => {
22898
- this.updatePopupPosition();
22899
- this.positioning = "no";
22900
- this.ref.markForCheck();
22897
+ if (this.currentTriggerElement) {
22898
+ /** @type {?} */
22899
+ const triggerRect = this.currentTriggerElement.getBoundingClientRect();
22900
+ /** @type {?} */
22901
+ const popupSize = {
22902
+ width: (/** @type {?} */ (this._width)),
22903
+ height: (/** @type {?} */ (this._height)),
22904
+ };
22905
+ /** @type {?} */
22906
+ const targetInfo = this.placementInfo;
22907
+ /** @type {?} */
22908
+ let finalPlacement = targetInfo.placement;
22909
+ /** @type {?} */
22910
+ const finalPosition = targetInfo.position;
22911
+ if (this.autoAdjust) {
22912
+ /** @type {?} */
22913
+ const popupRect = (/** @type {?} */ ({
22914
+ width: popupSize.width,
22915
+ height: popupSize.height,
22916
+ top: 0,
22917
+ left: 0,
22918
+ right: popupSize.width,
22919
+ bottom: popupSize.height,
22920
+ x: 0,
22921
+ y: 0,
22922
+ toJSON: (/**
22923
+ * @return {?}
22924
+ */
22925
+ () => ({})),
22926
+ }));
22927
+ /** @type {?} */
22928
+ const optimal = this.calculateOptimalPlacement(triggerRect, popupRect, window.innerWidth, window.innerHeight);
22929
+ finalPlacement = optimal.placement;
22930
+ }
22931
+ this.actualPlacement = finalPlacement;
22932
+ this.actualPosition = finalPosition;
22933
+ /** @type {?} */
22934
+ const position = this.calculatePositionCoordinates(triggerRect, finalPlacement, finalPosition, popupSize);
22935
+ if (this.popupElement && this.popupElement.nativeElement) {
22936
+ /** @type {?} */
22937
+ const popupEl = this.popupElement.nativeElement;
22938
+ popupEl.style.top = position.top;
22939
+ popupEl.style.left = position.left;
22940
+ popupEl.style.transform = `translate(${position.translateX}, ${position.translateY})`;
22941
+ popupEl.offsetHeight; // 强制同步应用样式
22942
+ // 位置设置完成后,再显示弹窗
22943
+ requestAnimationFrame((/**
22944
+ * @return {?}
22945
+ */
22946
+ () => {
22947
+ this.positioning = "no";
22948
+ this.ref.markForCheck();
22949
+ }));
22950
+ }
22951
+ }
22901
22952
  }));
22902
- }));
22953
+ }
22954
+ else {
22955
+ // 使用新的打开方式更新位置
22956
+ this.positioning = "yes";
22957
+ this.ref.markForCheck();
22958
+ requestAnimationFrame((/**
22959
+ * @return {?}
22960
+ */
22961
+ () => {
22962
+ requestAnimationFrame((/**
22963
+ * @return {?}
22964
+ */
22965
+ () => {
22966
+ this.updatePopupPosition();
22967
+ this.positioning = "no";
22968
+ this.ref.markForCheck();
22969
+ }));
22970
+ }));
22971
+ }
22903
22972
  }
22904
22973
  }
22905
22974
  // 处理 open 变化(确保在触发元素更新后处理)
@@ -23015,22 +23084,92 @@ class SmartPopupComponent {
23015
23084
  console.warn("SmartPopupComponent: No trigger element found");
23016
23085
  return;
23017
23086
  }
23018
- // 如果已经打开,先关闭再打开(更新位置)
23087
+ // 如果已经打开,且提供了固定尺寸,直接重新计算位置(避免关闭再打开导致的跳动)
23019
23088
  if (this.opened === "yes") {
23020
- // 先关闭弹窗
23021
- this.opened = "no";
23022
- this.positioning = "no";
23023
- this.ref.markForCheck();
23024
- // 立即在新位置打开
23025
- requestAnimationFrame((/**
23026
- * @return {?}
23027
- */
23028
- () => {
23029
- // 再次确保触发元素已更新
23030
- this.updateTriggerElement();
23031
- this.openPopup();
23032
- }));
23033
- return;
23089
+ if (this._width !== null && this._height !== null) {
23090
+ // 有固定尺寸,直接重新计算位置,不关闭弹窗
23091
+ // 先隐藏弹窗(positioning = yes)
23092
+ this.positioning = "yes";
23093
+ this.ref.markForCheck();
23094
+ this.ref.detectChanges(); // 立即应用隐藏状态
23095
+ requestAnimationFrame((/**
23096
+ * @return {?}
23097
+ */
23098
+ () => {
23099
+ // 再次确保触发元素已更新
23100
+ this.updateTriggerElement();
23101
+ if (this.currentTriggerElement) {
23102
+ /** @type {?} */
23103
+ const triggerRect = this.currentTriggerElement.getBoundingClientRect();
23104
+ /** @type {?} */
23105
+ const popupSize = {
23106
+ width: (/** @type {?} */ (this._width)),
23107
+ height: (/** @type {?} */ (this._height)),
23108
+ };
23109
+ /** @type {?} */
23110
+ const targetInfo = this.placementInfo;
23111
+ /** @type {?} */
23112
+ let finalPlacement = targetInfo.placement;
23113
+ /** @type {?} */
23114
+ const finalPosition = targetInfo.position;
23115
+ if (this.autoAdjust) {
23116
+ /** @type {?} */
23117
+ const popupRect = (/** @type {?} */ ({
23118
+ width: popupSize.width,
23119
+ height: popupSize.height,
23120
+ top: 0,
23121
+ left: 0,
23122
+ right: popupSize.width,
23123
+ bottom: popupSize.height,
23124
+ x: 0,
23125
+ y: 0,
23126
+ toJSON: (/**
23127
+ * @return {?}
23128
+ */
23129
+ () => ({})),
23130
+ }));
23131
+ /** @type {?} */
23132
+ const optimal = this.calculateOptimalPlacement(triggerRect, popupRect, window.innerWidth, window.innerHeight);
23133
+ finalPlacement = optimal.placement;
23134
+ }
23135
+ this.actualPlacement = finalPlacement;
23136
+ this.actualPosition = finalPosition;
23137
+ /** @type {?} */
23138
+ const position = this.calculatePositionCoordinates(triggerRect, finalPlacement, finalPosition, popupSize);
23139
+ if (this.popupElement && this.popupElement.nativeElement) {
23140
+ /** @type {?} */
23141
+ const popupEl = this.popupElement.nativeElement;
23142
+ popupEl.style.top = position.top;
23143
+ popupEl.style.left = position.left;
23144
+ popupEl.style.transform = `translate(${position.translateX}, ${position.translateY})`;
23145
+ popupEl.offsetHeight; // 强制同步应用样式
23146
+ // 位置设置完成后,再显示弹窗
23147
+ requestAnimationFrame((/**
23148
+ * @return {?}
23149
+ */
23150
+ () => {
23151
+ this.positioning = "no";
23152
+ this.ref.markForCheck();
23153
+ }));
23154
+ }
23155
+ }
23156
+ }));
23157
+ return;
23158
+ }
23159
+ else {
23160
+ // 没有固定尺寸,先关闭再打开
23161
+ this.opened = "no";
23162
+ this.positioning = "no";
23163
+ this.ref.markForCheck();
23164
+ requestAnimationFrame((/**
23165
+ * @return {?}
23166
+ */
23167
+ () => {
23168
+ this.updateTriggerElement();
23169
+ this.openPopup();
23170
+ }));
23171
+ return;
23172
+ }
23034
23173
  }
23035
23174
  // 确保触发元素已更新
23036
23175
  this.updateTriggerElement();
@@ -23080,13 +23219,15 @@ class SmartPopupComponent {
23080
23219
  const targetInfo = this.placementInfo;
23081
23220
  /** @type {?} */
23082
23221
  let finalPlacement = targetInfo.placement;
23222
+ // 始终使用用户指定的 position,autoAdjust 只调整 placement(方向),不改变对齐方式
23083
23223
  /** @type {?} */
23084
- let finalPosition = targetInfo.position;
23224
+ const finalPosition = targetInfo.position;
23085
23225
  if (this.autoAdjust) {
23086
23226
  /** @type {?} */
23087
23227
  const optimal = this.calculateOptimalPlacement(triggerRect, popupRect, viewportWidth, viewportHeight);
23228
+ // autoAdjust 只调整 placement(上下左右),保持用户指定的 position(对齐方式)
23088
23229
  finalPlacement = optimal.placement;
23089
- finalPosition = optimal.position;
23230
+ // finalPosition 保持用户指定的值,不改变
23090
23231
  }
23091
23232
  this.actualPlacement = finalPlacement;
23092
23233
  this.actualPosition = finalPosition;
@@ -23094,56 +23235,57 @@ class SmartPopupComponent {
23094
23235
  /** @type {?} */
23095
23236
  const position = this.calculatePositionCoordinates(triggerRect, finalPlacement, finalPosition, popupSize);
23096
23237
  // 先设置打开状态,但保持隐藏(positioning = yes)
23097
- this.opened = "yes";
23238
+ // 注意:先设置 positioning = yes,再设置 opened = yes,确保弹窗在位置计算完成前始终隐藏
23098
23239
  this.positioning = "yes";
23240
+ this.opened = "yes";
23099
23241
  this.openChange.emit(true);
23100
23242
  this.ref.markForCheck();
23101
- // 强制更新视图,确保DOM已更新
23243
+ // 强制更新视图,确保DOM已更新,但弹窗仍然隐藏(因为 positioning = yes)
23102
23244
  this.ref.detectChanges();
23103
- // 如果元素已经存在,立即设置位置(不等待 requestAnimationFrame)
23245
+ // 设置位置的函数
23246
+ /** @type {?} */
23247
+ const setPosition = (/**
23248
+ * @return {?}
23249
+ */
23250
+ () => {
23251
+ if (this.popupElement && this.popupElement.nativeElement) {
23252
+ /** @type {?} */
23253
+ const popupEl = this.popupElement.nativeElement;
23254
+ // 立即设置尺寸和位置
23255
+ popupEl.style.width = this._width + "px";
23256
+ popupEl.style.height = this._height + "px";
23257
+ popupEl.style.minWidth = this._width + "px";
23258
+ popupEl.style.maxWidth = this._width + "px";
23259
+ popupEl.style.minHeight = this._height + "px";
23260
+ popupEl.style.maxHeight = this._height + "px";
23261
+ popupEl.style.top = position.top;
23262
+ popupEl.style.left = position.left;
23263
+ popupEl.style.transform = `translate(${position.translateX}, ${position.translateY})`;
23264
+ // 强制同步应用样式,确保位置已设置
23265
+ popupEl.offsetHeight;
23266
+ // 位置设置完成后,再显示弹窗(设置 positioning = no)
23267
+ // 使用 requestAnimationFrame 确保样式已应用
23268
+ requestAnimationFrame((/**
23269
+ * @return {?}
23270
+ */
23271
+ () => {
23272
+ this.positioning = "no";
23273
+ this.ref.markForCheck();
23274
+ }));
23275
+ }
23276
+ });
23277
+ // 如果元素已经存在,立即设置位置
23104
23278
  if (this.popupElement && this.popupElement.nativeElement) {
23105
- /** @type {?} */
23106
- const popupEl = this.popupElement.nativeElement;
23107
- // 立即设置尺寸和位置
23108
- popupEl.style.width = this._width + "px";
23109
- popupEl.style.height = this._height + "px";
23110
- popupEl.style.minWidth = this._width + "px";
23111
- popupEl.style.maxWidth = this._width + "px";
23112
- popupEl.style.minHeight = this._height + "px";
23113
- popupEl.style.maxHeight = this._height + "px";
23114
- popupEl.style.top = position.top;
23115
- popupEl.style.left = position.left;
23116
- popupEl.style.transform = `translate(${position.translateX}, ${position.translateY})`;
23117
- // 强制同步应用样式
23118
- popupEl.offsetHeight;
23119
- // 位置设置完成后,立即显示弹窗
23120
- this.positioning = "no";
23121
- this.ref.markForCheck();
23279
+ setPosition();
23122
23280
  }
23123
23281
  else {
23124
- // 如果元素还不存在,等待渲染
23282
+ // 如果元素还不存在,等待渲染后再设置位置
23125
23283
  requestAnimationFrame((/**
23126
23284
  * @return {?}
23127
23285
  */
23128
23286
  () => {
23129
23287
  if (this.popupElement && this.popupElement.nativeElement) {
23130
- /** @type {?} */
23131
- const popupEl = this.popupElement.nativeElement;
23132
- // 立即设置尺寸和位置
23133
- popupEl.style.width = this._width + "px";
23134
- popupEl.style.height = this._height + "px";
23135
- popupEl.style.minWidth = this._width + "px";
23136
- popupEl.style.maxWidth = this._width + "px";
23137
- popupEl.style.minHeight = this._height + "px";
23138
- popupEl.style.maxHeight = this._height + "px";
23139
- popupEl.style.top = position.top;
23140
- popupEl.style.left = position.left;
23141
- popupEl.style.transform = `translate(${position.translateX}, ${position.translateY})`;
23142
- // 强制同步应用样式
23143
- popupEl.offsetHeight;
23144
- // 位置设置完成后,立即显示弹窗
23145
- this.positioning = "no";
23146
- this.ref.markForCheck();
23288
+ setPosition();
23147
23289
  }
23148
23290
  }));
23149
23291
  }
@@ -23275,13 +23417,15 @@ class SmartPopupComponent {
23275
23417
  const targetInfo = this.placementInfo;
23276
23418
  /** @type {?} */
23277
23419
  let finalPlacement = targetInfo.placement;
23420
+ // 始终使用用户指定的 position,autoAdjust 只调整 placement(方向),不改变对齐方式
23278
23421
  /** @type {?} */
23279
- let finalPosition = targetInfo.position;
23422
+ const finalPosition = targetInfo.position;
23280
23423
  if (this.autoAdjust) {
23281
23424
  /** @type {?} */
23282
23425
  const optimal = this.calculateOptimalPlacement(triggerRect, popupRect, viewportWidth, viewportHeight);
23426
+ // autoAdjust 只调整 placement(上下左右),保持用户指定的 position(对齐方式)
23283
23427
  finalPlacement = optimal.placement;
23284
- finalPosition = optimal.position;
23428
+ // finalPosition 保持用户指定的值,不改变
23285
23429
  }
23286
23430
  this.actualPlacement = finalPlacement;
23287
23431
  this.actualPosition = finalPosition;
@@ -23332,13 +23476,15 @@ class SmartPopupComponent {
23332
23476
  const targetInfo = this.placementInfo;
23333
23477
  /** @type {?} */
23334
23478
  let finalPlacement = targetInfo.placement;
23479
+ // 始终使用用户指定的 position,autoAdjust 只调整 placement(方向),不改变对齐方式
23335
23480
  /** @type {?} */
23336
- let finalPosition = targetInfo.position;
23481
+ const finalPosition = targetInfo.position;
23337
23482
  if (this.autoAdjust) {
23338
23483
  /** @type {?} */
23339
23484
  const optimal = this.calculateOptimalPlacement(triggerRect, popupRect, viewportWidth, viewportHeight);
23485
+ // autoAdjust 只调整 placement(上下左右),保持用户指定的 position(对齐方式)
23340
23486
  finalPlacement = optimal.placement;
23341
- finalPosition = optimal.position;
23487
+ // finalPosition 保持用户指定的值,不改变
23342
23488
  }
23343
23489
  this.actualPlacement = finalPlacement;
23344
23490
  this.actualPosition = finalPosition;
@@ -23499,17 +23645,12 @@ class SmartPopupComponent {
23499
23645
  });
23500
23646
  // 如果首选位置有足够空间,优先使用用户指定的position
23501
23647
  if (hasEnoughSpace(preferred)) {
23502
- // 检查用户指定的position是否有足够空间
23503
- if (checkPositionSpace(preferred, preferredPosition)) {
23504
- return {
23505
- placement: preferred,
23506
- position: preferredPosition,
23507
- };
23508
- }
23509
- // 如果指定的position空间不足,自动计算最佳position
23648
+ // 即使指定的position空间不足,也尽量保持用户指定的position
23649
+ // 边界约束逻辑会在 calculatePositionCoordinates 中处理位置调整
23650
+ // 这样可以保持用户期望的对齐方式
23510
23651
  return {
23511
23652
  placement: preferred,
23512
- position: calculatePosition(preferred),
23653
+ position: preferredPosition,
23513
23654
  };
23514
23655
  }
23515
23656
  // 根据首选位置选择备选位置
@@ -23550,6 +23691,7 @@ class SmartPopupComponent {
23550
23691
  };
23551
23692
  }
23552
23693
  // 计算位置坐标(纯计算,不依赖DOM,包含边界约束)
23694
+ // 注意:弹窗使用 position: fixed,所以坐标应该是相对于视口的,不需要加上滚动偏移
23553
23695
  /**
23554
23696
  * @private
23555
23697
  * @param {?} triggerRect
@@ -23568,10 +23710,6 @@ class SmartPopupComponent {
23568
23710
  /** @type {?} */
23569
23711
  const viewportHeight = window.innerHeight;
23570
23712
  /** @type {?} */
23571
- const scrollX = window.scrollX;
23572
- /** @type {?} */
23573
- const scrollY = window.scrollY;
23574
- /** @type {?} */
23575
23713
  let top = 0;
23576
23714
  /** @type {?} */
23577
23715
  let left = 0;
@@ -23581,109 +23719,268 @@ class SmartPopupComponent {
23581
23719
  let translateY = "0";
23582
23720
  switch (placement) {
23583
23721
  case "top":
23584
- top = triggerRect.top + scrollY - this.offset;
23722
+ // 弹窗在触发元素上方
23723
+ top = triggerRect.top - this.offset;
23585
23724
  translateY = "-100%";
23586
23725
  if (position === "start") {
23587
- left = triggerRect.left + scrollX;
23726
+ // 左对齐
23727
+ left = triggerRect.left;
23728
+ translateX = "0";
23588
23729
  }
23589
23730
  else if (position === "center") {
23590
- left = triggerRect.left + scrollX + triggerWidth / 2;
23731
+ // 居中对齐
23732
+ left = triggerRect.left + triggerWidth / 2;
23591
23733
  translateX = "-50%";
23592
23734
  }
23593
23735
  else {
23594
- left = triggerRect.left + scrollX + triggerWidth;
23736
+ // 右对齐(end)
23737
+ left = triggerRect.right;
23595
23738
  translateX = "-100%";
23596
23739
  }
23597
23740
  // 边界约束:确保弹窗不超出视口
23598
- if (top - popupSize.height < scrollY) {
23599
- top = scrollY + 8; // 距离顶部至少8px
23741
+ /** @type {?} */
23742
+ const popupTop = top - popupSize.height;
23743
+ if (popupTop < 0) {
23744
+ top = popupSize.height + 8; // 距离顶部至少8px
23745
+ translateY = "0"; // 取消向上偏移
23600
23746
  }
23601
- if (left < scrollX) {
23602
- left = scrollX + 8;
23603
- translateX = "0";
23747
+ // 水平边界约束:尽量保持原始对齐方式
23748
+ if (position === "start") {
23749
+ // 左对齐:如果超出右边界,调整到右边界;如果超出左边界,调整到左边界
23750
+ if (left + popupSize.width > viewportWidth) {
23751
+ left = viewportWidth - popupSize.width - 8;
23752
+ }
23753
+ if (left < 8) {
23754
+ left = 8;
23755
+ }
23604
23756
  }
23605
- else if (left + popupSize.width > scrollX + viewportWidth) {
23606
- left = scrollX + viewportWidth - popupSize.width - 8;
23607
- translateX = "0";
23757
+ else if (position === "center") {
23758
+ // 居中对齐:计算实际左边界
23759
+ /** @type {?} */
23760
+ const actualLeft = left - popupSize.width / 2;
23761
+ if (actualLeft < 8) {
23762
+ // 左边界不足,尽量保持居中,但调整到不超出左边界
23763
+ left = 8 + popupSize.width / 2;
23764
+ translateX = "-50%";
23765
+ }
23766
+ else if (actualLeft + popupSize.width > viewportWidth - 8) {
23767
+ // 右边界不足,尽量保持居中,但调整到不超出右边界
23768
+ left = viewportWidth - 8 - popupSize.width / 2;
23769
+ translateX = "-50%";
23770
+ }
23771
+ }
23772
+ else {
23773
+ // 右对齐(end):弹窗右边缘与触发元素右边缘对齐
23774
+ // left 是弹窗的右边缘位置(因为 translateX = "-100%")
23775
+ // 弹窗的左边缘在 left - popupSize.width
23776
+ /** @type {?} */
23777
+ const popupLeftEdge = left - popupSize.width;
23778
+ // 如果弹窗左边缘超出左边界,调整位置但保持右对齐
23779
+ if (popupLeftEdge < 8) {
23780
+ left = 8 + popupSize.width;
23781
+ translateX = "-100%";
23782
+ }
23783
+ // 如果弹窗右边缘超出右边界,调整位置但保持右对齐
23784
+ if (left > viewportWidth - 8) {
23785
+ left = viewportWidth - 8;
23786
+ translateX = "-100%";
23787
+ }
23608
23788
  }
23609
23789
  break;
23610
23790
  case "bottom":
23611
- top = triggerRect.bottom + scrollY + this.offset;
23791
+ // 弹窗在触发元素下方
23792
+ top = triggerRect.bottom + this.offset;
23612
23793
  if (position === "start") {
23613
- left = triggerRect.left + scrollX;
23794
+ // 左对齐
23795
+ left = triggerRect.left;
23796
+ translateX = "0";
23614
23797
  }
23615
23798
  else if (position === "center") {
23616
- left = triggerRect.left + scrollX + triggerWidth / 2;
23799
+ // 居中对齐
23800
+ left = triggerRect.left + triggerWidth / 2;
23617
23801
  translateX = "-50%";
23618
23802
  }
23619
23803
  else {
23620
- left = triggerRect.left + scrollX + triggerWidth;
23804
+ // 右对齐(end)
23805
+ left = triggerRect.right;
23621
23806
  translateX = "-100%";
23622
23807
  }
23623
23808
  // 边界约束:确保弹窗不超出视口
23624
- if (top + popupSize.height > scrollY + viewportHeight) {
23625
- top = scrollY + viewportHeight - popupSize.height - 8; // 距离底部至少8px
23809
+ if (top + popupSize.height > viewportHeight) {
23810
+ top = viewportHeight - popupSize.height - 8; // 距离底部至少8px
23626
23811
  }
23627
- if (left < scrollX) {
23628
- left = scrollX + 8;
23629
- translateX = "0";
23812
+ // 水平边界约束:尽量保持原始对齐方式
23813
+ if (position === "start") {
23814
+ // 左对齐:如果超出右边界,调整到右边界;如果超出左边界,调整到左边界
23815
+ if (left + popupSize.width > viewportWidth) {
23816
+ left = viewportWidth - popupSize.width - 8;
23817
+ }
23818
+ if (left < 8) {
23819
+ left = 8;
23820
+ }
23630
23821
  }
23631
- else if (left + popupSize.width > scrollX + viewportWidth) {
23632
- left = scrollX + viewportWidth - popupSize.width - 8;
23633
- translateX = "0";
23822
+ else if (position === "center") {
23823
+ // 居中对齐:计算实际左边界
23824
+ /** @type {?} */
23825
+ const actualLeft = left - popupSize.width / 2;
23826
+ if (actualLeft < 8) {
23827
+ // 左边界不足,尽量保持居中,但调整到不超出左边界
23828
+ left = 8 + popupSize.width / 2;
23829
+ translateX = "-50%";
23830
+ }
23831
+ else if (actualLeft + popupSize.width > viewportWidth - 8) {
23832
+ // 右边界不足,尽量保持居中,但调整到不超出右边界
23833
+ left = viewportWidth - 8 - popupSize.width / 2;
23834
+ translateX = "-50%";
23835
+ }
23836
+ }
23837
+ else {
23838
+ // 右对齐(end):弹窗右边缘与触发元素右边缘对齐
23839
+ // left 是弹窗的右边缘位置(因为 translateX = "-100%")
23840
+ // 弹窗的左边缘在 left - popupSize.width
23841
+ /** @type {?} */
23842
+ const popupLeftEdge = left - popupSize.width;
23843
+ // 优先保持与触发元素右对齐,只在必要时调整
23844
+ // 如果弹窗右边缘超出右边界,调整位置但保持右对齐
23845
+ if (left > viewportWidth - 8) {
23846
+ left = viewportWidth - 8;
23847
+ translateX = "-100%";
23848
+ }
23849
+ // 如果弹窗左边缘超出左边界,且弹窗可以完全显示在视口内,才调整位置
23850
+ // 这样可以确保在可能的情况下,弹窗始终与触发元素右对齐
23851
+ if (popupLeftEdge < 8 && popupSize.width < viewportWidth - 16) {
23852
+ // 只有当弹窗可以完全显示在视口内时,才调整位置
23853
+ // 否则保持与触发元素右对齐,即使部分超出左边界
23854
+ left = 8 + popupSize.width;
23855
+ translateX = "-100%";
23856
+ }
23634
23857
  }
23635
23858
  break;
23636
23859
  case "left":
23637
- left = triggerRect.left + scrollX - this.offset;
23860
+ // 弹窗在触发元素左侧
23861
+ left = triggerRect.left - this.offset;
23638
23862
  translateX = "-100%";
23639
23863
  if (position === "start") {
23640
- top = triggerRect.top + scrollY;
23864
+ // 上对齐
23865
+ top = triggerRect.top;
23866
+ translateY = "0";
23641
23867
  }
23642
23868
  else if (position === "center") {
23643
- top = triggerRect.top + scrollY + triggerHeight / 2;
23869
+ // 居中对齐
23870
+ top = triggerRect.top + triggerHeight / 2;
23644
23871
  translateY = "-50%";
23645
23872
  }
23646
23873
  else {
23647
- top = triggerRect.top + scrollY + triggerHeight;
23874
+ // 下对齐(end)
23875
+ top = triggerRect.bottom;
23648
23876
  translateY = "-100%";
23649
23877
  }
23650
23878
  // 边界约束:确保弹窗不超出视口
23651
- if (left - popupSize.width < scrollX) {
23652
- left = scrollX + 8; // 距离左侧至少8px
23879
+ /** @type {?} */
23880
+ const popupLeftSide = left - popupSize.width;
23881
+ if (popupLeftSide < 0) {
23882
+ left = 8; // 距离左侧至少8px
23883
+ translateX = "0"; // 取消向左偏移
23653
23884
  }
23654
- if (top < scrollY) {
23655
- top = scrollY + 8;
23656
- translateY = "0";
23885
+ // 垂直边界约束:尽量保持原始对齐方式
23886
+ if (position === "start") {
23887
+ // 上对齐:如果超出下边界,调整到下边界;如果超出上边界,调整到上边界
23888
+ if (top + popupSize.height > viewportHeight) {
23889
+ top = viewportHeight - popupSize.height - 8;
23890
+ }
23891
+ if (top < 8) {
23892
+ top = 8;
23893
+ }
23657
23894
  }
23658
- else if (top + popupSize.height > scrollY + viewportHeight) {
23659
- top = scrollY + viewportHeight - popupSize.height - 8;
23660
- translateY = "0";
23895
+ else if (position === "center") {
23896
+ // 居中对齐:计算实际上边界
23897
+ /** @type {?} */
23898
+ const actualTop = top - popupSize.height / 2;
23899
+ if (actualTop < 8) {
23900
+ // 上边界不足,尽量保持居中,但调整到不超出上边界
23901
+ top = 8 + popupSize.height / 2;
23902
+ translateY = "-50%";
23903
+ }
23904
+ else if (actualTop + popupSize.height > viewportHeight - 8) {
23905
+ // 下边界不足,尽量保持居中,但调整到不超出下边界
23906
+ top = viewportHeight - 8 - popupSize.height / 2;
23907
+ translateY = "-50%";
23908
+ }
23909
+ }
23910
+ else {
23911
+ // 下对齐(end):如果超出上边界,调整到上边界;如果超出下边界,调整到下边界
23912
+ /** @type {?} */
23913
+ const actualTop = top - popupSize.height;
23914
+ if (actualTop < 8) {
23915
+ top = 8 + popupSize.height;
23916
+ translateY = "-100%";
23917
+ }
23918
+ if (top > viewportHeight - 8) {
23919
+ top = viewportHeight - 8;
23920
+ translateY = "-100%";
23921
+ }
23661
23922
  }
23662
23923
  break;
23663
23924
  case "right":
23664
- left = triggerRect.right + scrollX + this.offset;
23925
+ // 弹窗在触发元素右侧
23926
+ left = triggerRect.right + this.offset;
23927
+ translateX = "0";
23665
23928
  if (position === "start") {
23666
- top = triggerRect.top + scrollY;
23929
+ // 上对齐
23930
+ top = triggerRect.top;
23931
+ translateY = "0";
23667
23932
  }
23668
23933
  else if (position === "center") {
23669
- top = triggerRect.top + scrollY + triggerHeight / 2;
23934
+ // 居中对齐
23935
+ top = triggerRect.top + triggerHeight / 2;
23670
23936
  translateY = "-50%";
23671
23937
  }
23672
23938
  else {
23673
- top = triggerRect.top + scrollY + triggerHeight;
23939
+ // 下对齐(end)
23940
+ top = triggerRect.bottom;
23674
23941
  translateY = "-100%";
23675
23942
  }
23676
23943
  // 边界约束:确保弹窗不超出视口
23677
- if (left + popupSize.width > scrollX + viewportWidth) {
23678
- left = scrollX + viewportWidth - popupSize.width - 8; // 距离右侧至少8px
23944
+ if (left + popupSize.width > viewportWidth) {
23945
+ left = viewportWidth - popupSize.width - 8; // 距离右侧至少8px
23679
23946
  }
23680
- if (top < scrollY) {
23681
- top = scrollY + 8;
23682
- translateY = "0";
23947
+ // 垂直边界约束:尽量保持原始对齐方式
23948
+ if (position === "start") {
23949
+ // 上对齐:如果超出下边界,调整到下边界;如果超出上边界,调整到上边界
23950
+ if (top + popupSize.height > viewportHeight) {
23951
+ top = viewportHeight - popupSize.height - 8;
23952
+ }
23953
+ if (top < 8) {
23954
+ top = 8;
23955
+ }
23683
23956
  }
23684
- else if (top + popupSize.height > scrollY + viewportHeight) {
23685
- top = scrollY + viewportHeight - popupSize.height - 8;
23686
- translateY = "0";
23957
+ else if (position === "center") {
23958
+ // 居中对齐:计算实际上边界
23959
+ /** @type {?} */
23960
+ const actualTop = top - popupSize.height / 2;
23961
+ if (actualTop < 8) {
23962
+ // 上边界不足,尽量保持居中,但调整到不超出上边界
23963
+ top = 8 + popupSize.height / 2;
23964
+ translateY = "-50%";
23965
+ }
23966
+ else if (actualTop + popupSize.height > viewportHeight - 8) {
23967
+ // 下边界不足,尽量保持居中,但调整到不超出下边界
23968
+ top = viewportHeight - 8 - popupSize.height / 2;
23969
+ translateY = "-50%";
23970
+ }
23971
+ }
23972
+ else {
23973
+ // 下对齐(end):如果超出上边界,调整到上边界;如果超出下边界,调整到下边界
23974
+ /** @type {?} */
23975
+ const actualTop = top - popupSize.height;
23976
+ if (actualTop < 8) {
23977
+ top = 8 + popupSize.height;
23978
+ translateY = "-100%";
23979
+ }
23980
+ if (top > viewportHeight - 8) {
23981
+ top = viewportHeight - 8;
23982
+ translateY = "-100%";
23983
+ }
23687
23984
  }
23688
23985
  break;
23689
23986
  }
@@ -23738,7 +24035,7 @@ SmartPopupComponent.decorators = [
23738
24035
  { type: Component, args: [{
23739
24036
  selector: "rs-smart-popup",
23740
24037
  template: "<!-- \u89E6\u53D1\u5143\u7D20\u63D2\u69FD -->\r\n<ng-container #trigger>\r\n <ng-content select=\"[trigger]\"></ng-content>\r\n</ng-container>\r\n\r\n<!-- \u5F39\u7A97\u5185\u5BB9 -->\r\n<div\r\n #popup\r\n class=\"rs-smart-popup-content\"\r\n [attr.data-opened]=\"opened\"\r\n [attr.data-positioning]=\"positioning\"\r\n (wheel)=\"onPopupWheel($event)\"\r\n (scroll)=\"onPopupScroll($event)\"\r\n>\r\n <div *ngIf=\"loading\" class=\"rs-smart-popup-loading\">\r\n <div class=\"loading-spinner\"></div>\r\n </div>\r\n <div *ngIf=\"!loading\" class=\"rs-smart-popup-body\">\r\n <ng-content></ng-content>\r\n </div>\r\n</div>\r\n\r\n",
23741
- styles: [".rs-smart-popup-trigger{display:inline-block;cursor:pointer}::ng-deep #rs-smart-popup-fixed-container{width:0;height:0;z-index:100000;pointer-events:none}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content{pointer-events:auto;position:fixed;z-index:100001;padding:8px;border-radius:8px;background:#fff;box-shadow:0 0 8px 0 rgba(0,0,0,.25);min-width:200px;max-width:400px;max-height:500px;overflow:auto}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=no]{display:none}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=yes]{display:block}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=yes][data-positioning=yes]{display:none!important}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=yes][data-positioning=no]{display:block;visibility:visible;opacity:1;pointer-events:auto;-webkit-animation:.2s ease-out popup-fade-in;animation:.2s ease-out popup-fade-in}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;min-height:100px}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-loading .loading-spinner{width:24px;height:24px;border:3px solid #f3f3f3;border-top:3px solid #3498db;border-radius:50%;-webkit-animation:1s linear infinite spin;animation:1s linear infinite spin;margin-bottom:12px}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-loading .loading-text{color:#666;font-size:14px}@-webkit-keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-body{width:100%}@-webkit-keyframes popup-fade-in{from{opacity:0;transform:translate(var(--translate-x,0),var(--translate-y,0)) scale(.95)}to{opacity:1;transform:translate(var(--translate-x,0),var(--translate-y,0)) scale(1)}}@keyframes popup-fade-in{from{opacity:0;transform:translate(var(--translate-x,0),var(--translate-y,0)) scale(.95)}to{opacity:1;transform:translate(var(--translate-x,0),var(--translate-y,0)) scale(1)}}"]
24038
+ styles: [".rs-smart-popup-trigger{display:inline-block;cursor:pointer}::ng-deep #rs-smart-popup-fixed-container{width:0;height:0;z-index:100000;pointer-events:none}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content{pointer-events:auto;position:fixed;z-index:100001;padding:8px;border-radius:8px;background:#fff;box-shadow:0 0 8px 0 rgba(0,0,0,.25);min-width:200px;max-width:400px;max-height:500px;overflow:auto}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=no]{display:none}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=yes]{display:block}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=yes][data-positioning=yes]{display:none!important}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content[data-opened=yes][data-positioning=no]{display:block;visibility:visible;opacity:1;pointer-events:auto}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:20px;min-height:100px}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-loading .loading-spinner{width:24px;height:24px;border:3px solid #f3f3f3;border-top:3px solid #3498db;border-radius:50%;-webkit-animation:1s linear infinite spin;animation:1s linear infinite spin;margin-bottom:12px}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-loading .loading-text{color:#666;font-size:14px}@-webkit-keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}::ng-deep #rs-smart-popup-fixed-container .rs-smart-popup-content .rs-smart-popup-body{width:100%}"]
23742
24039
  }] }
23743
24040
  ];
23744
24041
  /** @nocollapse */