@douyinfe/semi-foundation 2.98.0 → 2.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cascader/foundation.ts +3 -1
- package/form/foundation.ts +2 -0
- package/lib/cjs/cascader/foundation.js +4 -1
- package/lib/cjs/form/foundation.js +2 -0
- package/lib/cjs/popover/popover.css +4 -4
- package/lib/cjs/table/rtl.scss +21 -0
- package/lib/cjs/table/table.css +28 -0
- package/lib/cjs/table/table.scss +32 -0
- package/lib/cjs/tooltip/arrow.scss +4 -4
- package/lib/cjs/tooltip/foundation.js +64 -0
- package/lib/cjs/tooltip/tooltip.css +4 -4
- package/lib/es/cascader/foundation.js +4 -1
- package/lib/es/form/foundation.js +2 -0
- package/lib/es/popover/popover.css +4 -4
- package/lib/es/table/rtl.scss +21 -0
- package/lib/es/table/table.css +28 -0
- package/lib/es/table/table.scss +32 -0
- package/lib/es/tooltip/arrow.scss +4 -4
- package/lib/es/tooltip/foundation.js +64 -0
- package/lib/es/tooltip/tooltip.css +4 -4
- package/package.json +4 -4
- package/table/rtl.scss +21 -0
- package/table/table.scss +32 -0
- package/tooltip/arrow.scss +4 -4
- package/tooltip/foundation.ts +78 -0
package/cascader/foundation.ts
CHANGED
|
@@ -650,7 +650,9 @@ export default class CascaderFoundation extends BaseFoundation<CascaderAdapter,
|
|
|
650
650
|
} else if (selectedKeys.size && !multiple) {
|
|
651
651
|
inputValue = this.renderDisplayText([...selectedKeys][0]);
|
|
652
652
|
}
|
|
653
|
-
|
|
653
|
+
// Reset isSearching immediately in close() instead of relying on afterClose callback
|
|
654
|
+
// This prevents timing issues where open() clears inputValue but isSearching is still true
|
|
655
|
+
this._adapter.updateStates({ inputValue, isSearching: false });
|
|
654
656
|
!multiple && this.toggle2SearchInput(false);
|
|
655
657
|
!multiple && this._adapter.updateFocusState(false);
|
|
656
658
|
}
|
package/form/foundation.ts
CHANGED
|
@@ -520,6 +520,8 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
|
|
|
520
520
|
const formAllowEmpty = this.getProp('allowEmpty');
|
|
521
521
|
|
|
522
522
|
// priority at Field level
|
|
523
|
+
// NOTE: Keep legacy semantics here to avoid implicit breaking changes.
|
|
524
|
+
// (Field-level allowEmpty overriding behavior is handled during register/restore paths.)
|
|
523
525
|
const allowEmpty = fieldAllowEmpty ? fieldAllowEmpty : formAllowEmpty;
|
|
524
526
|
|
|
525
527
|
ObjectUtil.set(this.data.values, field, value, allowEmpty);
|
|
@@ -454,8 +454,11 @@ class CascaderFoundation extends _foundation.default {
|
|
|
454
454
|
} else if (selectedKeys.size && !multiple) {
|
|
455
455
|
inputValue = this.renderDisplayText([...selectedKeys][0]);
|
|
456
456
|
}
|
|
457
|
+
// Reset isSearching immediately in close() instead of relying on afterClose callback
|
|
458
|
+
// This prevents timing issues where open() clears inputValue but isSearching is still true
|
|
457
459
|
this._adapter.updateStates({
|
|
458
|
-
inputValue
|
|
460
|
+
inputValue,
|
|
461
|
+
isSearching: false
|
|
459
462
|
});
|
|
460
463
|
!multiple && this.toggle2SearchInput(false);
|
|
461
464
|
!multiple && this._adapter.updateFocusState(false);
|
|
@@ -493,6 +493,8 @@ class FormFoundation extends _foundation.default {
|
|
|
493
493
|
*/
|
|
494
494
|
const formAllowEmpty = this.getProp('allowEmpty');
|
|
495
495
|
// priority at Field level
|
|
496
|
+
// NOTE: Keep legacy semantics here to avoid implicit breaking changes.
|
|
497
|
+
// (Field-level allowEmpty overriding behavior is handled during register/restore paths.)
|
|
496
498
|
const allowEmpty = fieldAllowEmpty ? fieldAllowEmpty : formAllowEmpty;
|
|
497
499
|
ObjectUtil.set(this.data.values, field, value, allowEmpty);
|
|
498
500
|
/**
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
color: unset;
|
|
77
77
|
}
|
|
78
78
|
.semi-popover-wrapper[x-placement=top] .semi-popover-icon-arrow {
|
|
79
|
-
left: 50
|
|
79
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
80
80
|
transform: translateX(-50%);
|
|
81
81
|
bottom: -7px;
|
|
82
82
|
}
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
width: 8px;
|
|
115
115
|
height: 24px;
|
|
116
116
|
right: -7px;
|
|
117
|
-
top: 50
|
|
117
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
118
118
|
transform: translateY(-50%);
|
|
119
119
|
}
|
|
120
120
|
.semi-popover-wrapper[x-placement=left].semi-popover-with-arrow,
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
width: 8px;
|
|
147
147
|
height: 24px;
|
|
148
148
|
left: -7px;
|
|
149
|
-
top: 50
|
|
149
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
150
150
|
transform: translateY(-50%) rotate(180deg);
|
|
151
151
|
}
|
|
152
152
|
.semi-popover-wrapper[x-placement=right].semi-popover-with-arrow,
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
}
|
|
176
176
|
.semi-popover-wrapper[x-placement=bottom] .semi-popover-icon-arrow {
|
|
177
177
|
top: -7px;
|
|
178
|
-
left: 50
|
|
178
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
179
179
|
transform: translateX(-50%) rotate(180deg);
|
|
180
180
|
}
|
|
181
181
|
.semi-popover-wrapper[x-placement=bottom].semi-popover-with-arrow,
|
package/lib/cjs/table/rtl.scss
CHANGED
|
@@ -144,6 +144,27 @@ $module: #{$prefix}-table;
|
|
|
144
144
|
border-left: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
// Fix #441 (RTL): when horizontal scroll is enabled and table is NOT scrolled to the far left,
|
|
149
|
+
// the outer left border can be outside of viewport (the real border is rendered by the last column cell).
|
|
150
|
+
// Draw an overlay left border on the container to keep it visible.
|
|
151
|
+
// When scrolled to the far left, `.semi-table-scroll-position-left` exists and we should not draw it.
|
|
152
|
+
&:not(.#{$module}-scroll-position-left) {
|
|
153
|
+
& > .#{$module}-container {
|
|
154
|
+
&::after {
|
|
155
|
+
content: '';
|
|
156
|
+
position: absolute;
|
|
157
|
+
top: 0;
|
|
158
|
+
left: 0;
|
|
159
|
+
bottom: 0;
|
|
160
|
+
width: $width-table_base_border;
|
|
161
|
+
background-color: $color-table-border-default;
|
|
162
|
+
display: block;
|
|
163
|
+
z-index: $z-table_fixed_column + 2;
|
|
164
|
+
pointer-events: none;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
147
168
|
}
|
|
148
169
|
|
|
149
170
|
&-fixed {
|
package/lib/cjs/table/table.css
CHANGED
|
@@ -416,6 +416,21 @@
|
|
|
416
416
|
border-right: 1px solid var(--semi-color-border);
|
|
417
417
|
border-bottom: 1px solid var(--semi-color-border);
|
|
418
418
|
}
|
|
419
|
+
.semi-table-bordered:not(.semi-table-scroll-position-right) > .semi-table-container::after {
|
|
420
|
+
content: "";
|
|
421
|
+
position: absolute;
|
|
422
|
+
top: 0;
|
|
423
|
+
right: 0;
|
|
424
|
+
bottom: 0;
|
|
425
|
+
width: 1px;
|
|
426
|
+
background-color: var(--semi-color-border);
|
|
427
|
+
display: block;
|
|
428
|
+
z-index: 103;
|
|
429
|
+
pointer-events: none;
|
|
430
|
+
}
|
|
431
|
+
.semi-table-bordered:not(.semi-table-scroll-position-right) > .semi-table-container > .semi-table-header {
|
|
432
|
+
box-shadow: inset-1px 0 0 0 var(--semi-color-border);
|
|
433
|
+
}
|
|
419
434
|
:where(.semi-table-bordered > .semi-table-container) > .semi-table-body > .semi-table-placeholder {
|
|
420
435
|
border-right: 1px solid var(--semi-color-border);
|
|
421
436
|
}
|
|
@@ -435,6 +450,7 @@
|
|
|
435
450
|
position: sticky;
|
|
436
451
|
left: 0px;
|
|
437
452
|
z-index: 1;
|
|
453
|
+
box-sizing: border-box;
|
|
438
454
|
padding: 16px 12px;
|
|
439
455
|
color: var(--semi-color-text-2);
|
|
440
456
|
font-size: 14px;
|
|
@@ -592,6 +608,18 @@
|
|
|
592
608
|
border-right: 0;
|
|
593
609
|
border-left: 1px solid var(--semi-color-border);
|
|
594
610
|
}
|
|
611
|
+
.semi-table-wrapper-rtl .semi-table-bordered:not(.semi-table-scroll-position-left) > .semi-table-container::after {
|
|
612
|
+
content: "";
|
|
613
|
+
position: absolute;
|
|
614
|
+
top: 0;
|
|
615
|
+
left: 0;
|
|
616
|
+
bottom: 0;
|
|
617
|
+
width: 1px;
|
|
618
|
+
background-color: var(--semi-color-border);
|
|
619
|
+
display: block;
|
|
620
|
+
z-index: 103;
|
|
621
|
+
pointer-events: none;
|
|
622
|
+
}
|
|
595
623
|
.semi-table-wrapper-rtl .semi-table-fixed > .semi-table-tbody > .semi-table-row-expand > .semi-table-row-cell > .semi-table-expand-inner, .semi-table-wrapper-rtl .semi-table-fixed > .semi-table-tbody > .semi-table-row-section > .semi-table-row-cell > .semi-table-section-inner {
|
|
596
624
|
left: auto;
|
|
597
625
|
right: 0;
|
package/lib/cjs/table/table.scss
CHANGED
|
@@ -555,6 +555,35 @@ $module: #{$prefix}-table;
|
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
// Fix #441: when horizontal scroll is enabled (e.g. scroll.x='101%') and table is NOT scrolled to the far right,
|
|
559
|
+
// the outer right border can be outside of viewport because the real right border is rendered by the
|
|
560
|
+
// last column cell border (which is scrollable). Draw an overlay right border on the container to keep it visible.
|
|
561
|
+
// When scrolled to the far right, `.semi-table-scroll-position-right` exists and we should not draw it to avoid
|
|
562
|
+
// double borders.
|
|
563
|
+
&:not(.#{$module}-scroll-position-right) {
|
|
564
|
+
& > .#{$module}-container {
|
|
565
|
+
&::after {
|
|
566
|
+
content: '';
|
|
567
|
+
position: absolute;
|
|
568
|
+
top: 0;
|
|
569
|
+
right: 0;
|
|
570
|
+
bottom: 0;
|
|
571
|
+
width: $width-table_base_border;
|
|
572
|
+
background-color: $color-table-border-default;
|
|
573
|
+
display: block;
|
|
574
|
+
// Make sure the overlay border stays above table content/fixed columns
|
|
575
|
+
z-index: $z-table_fixed_column + 2;
|
|
576
|
+
pointer-events: none;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Ensure header shows a visible right border even when the container overlay
|
|
580
|
+
// is covered by native scrollbars in some browsers.
|
|
581
|
+
& > .#{$module}-header {
|
|
582
|
+
box-shadow: inset -$width-table_base_border 0 0 0 $color-table-border-default;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
558
587
|
:where(& > .#{$module}-container) {
|
|
559
588
|
& > .#{$module}-body > .#{$module}-placeholder {
|
|
560
589
|
border-right: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
|
|
@@ -589,6 +618,9 @@ $module: #{$prefix}-table;
|
|
|
589
618
|
position: sticky;
|
|
590
619
|
left: 0px;
|
|
591
620
|
z-index: 1;
|
|
621
|
+
// In bordered mode, placeholder may receive an extra side border to complete the outer frame.
|
|
622
|
+
// Use border-box to avoid 1px horizontal overflow (and unwanted horizontal scrollbar) in empty data + scroll.y cases.
|
|
623
|
+
box-sizing: border-box;
|
|
592
624
|
padding: #{$spacing-table-paddingY} #{$spacing-table-paddingX};
|
|
593
625
|
color: $color-table_placeholder-text-default;
|
|
594
626
|
font-size: #{$font-table_base-fontSize};
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
&[x-placement='top'] {
|
|
25
25
|
.#{$module-icon} {
|
|
26
|
-
left: 50
|
|
26
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
27
27
|
transform: translateX(-50%);
|
|
28
28
|
bottom: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
|
|
29
29
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
width: $width-tooltip_arrow_vertical;
|
|
62
62
|
height: $height-tooltip_arrow_vertical;
|
|
63
63
|
right: (-$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x);
|
|
64
|
-
top: 50
|
|
64
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
65
65
|
transform: translateY(-50%);
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
width: $width-tooltip_arrow_vertical;
|
|
94
94
|
height: $height-tooltip_arrow_vertical;
|
|
95
95
|
left: -$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x;
|
|
96
|
-
top: 50
|
|
96
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
97
97
|
transform: translateY(-50%) rotate(180deg);
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
&[x-placement='bottom'] {
|
|
123
123
|
.#{$module-icon} {
|
|
124
124
|
top: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
|
|
125
|
-
left: 50
|
|
125
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
126
126
|
transform: translateX(-50%) rotate(180deg);
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -683,11 +683,75 @@ class Tooltip extends _foundation.default {
|
|
|
683
683
|
top = position.includes('Top') ? top + offsetY : top - offsetY;
|
|
684
684
|
}
|
|
685
685
|
}
|
|
686
|
+
// Handle arrowPointAtCenter for center positions (top/bottom/left/right)
|
|
687
|
+
// For center positions, the arrow needs to point at trigger center, not Popover center
|
|
688
|
+
let cssArrowOffsetX;
|
|
689
|
+
let cssArrowOffsetY;
|
|
690
|
+
if (showArrow) {
|
|
691
|
+
const isCenterPosition = ['top', 'bottom', 'left', 'right'].includes(position);
|
|
692
|
+
if (isCenterPosition) {
|
|
693
|
+
if (arrowPointAtCenter) {
|
|
694
|
+
// arrowPointAtCenter=true: arrow should point at trigger center
|
|
695
|
+
// Calculate arrow position relative to Popover left/top edge
|
|
696
|
+
if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
|
|
697
|
+
// Popover center is at `left`, trigger center is at `middleX`
|
|
698
|
+
// Arrow position from Popover left edge:
|
|
699
|
+
// = middleX - (left - wrapperRect.width/2)
|
|
700
|
+
// = middleX - left + wrapperRect.width/2
|
|
701
|
+
// Percentage: (middleX - left) / wrapperRect.width + 0.5
|
|
702
|
+
const arrowOffsetPercent = (middleX - left) / wrapperRect.width + 0.5;
|
|
703
|
+
// Clamp to valid range to prevent arrow going outside Popover
|
|
704
|
+
const minOffset = (horizontalArrowWidth / 2 + positionOffsetX) / wrapperRect.width;
|
|
705
|
+
const maxOffset = 1 - minOffset;
|
|
706
|
+
const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
|
|
707
|
+
// Only set CSS variable if different from default 50%
|
|
708
|
+
if (Math.abs(clampedOffset - 0.5) > 0.01) {
|
|
709
|
+
cssArrowOffsetX = `${clampedOffset * 100}%`;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
|
|
713
|
+
const arrowOffsetPercent = (middleY - top) / wrapperRect.height + 0.5;
|
|
714
|
+
const minOffset = (verticalArrowHeight / 2 + positionOffsetY) / wrapperRect.height;
|
|
715
|
+
const maxOffset = 1 - minOffset;
|
|
716
|
+
const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
|
|
717
|
+
if (Math.abs(clampedOffset - 0.5) > 0.01) {
|
|
718
|
+
cssArrowOffsetY = `${clampedOffset * 100}%`;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
} else {
|
|
722
|
+
// arrowPointAtCenter=false: arrow should be at Popover edge
|
|
723
|
+
// Determine which edge based on trigger position in viewport
|
|
724
|
+
if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
|
|
725
|
+
const offsetXWithArrow = positionOffsetX + horizontalArrowWidth / 2;
|
|
726
|
+
if (isTriggerNearLeft) {
|
|
727
|
+
cssArrowOffsetX = `${offsetXWithArrow / wrapperRect.width * 100}%`;
|
|
728
|
+
} else {
|
|
729
|
+
cssArrowOffsetX = `${(wrapperRect.width - offsetXWithArrow) / wrapperRect.width * 100}%`;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
|
|
733
|
+
const offsetYWithArrow = positionOffsetY + verticalArrowHeight / 2;
|
|
734
|
+
if (isTriggerNearTop) {
|
|
735
|
+
cssArrowOffsetY = `${offsetYWithArrow / wrapperRect.height * 100}%`;
|
|
736
|
+
} else {
|
|
737
|
+
cssArrowOffsetY = `${(wrapperRect.height - offsetYWithArrow) / wrapperRect.height * 100}%`;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
686
743
|
// The left/top value here must be rounded, otherwise it will cause the small triangle to shake
|
|
687
744
|
const style = {
|
|
688
745
|
left: this._roundPixel(left),
|
|
689
746
|
top: this._roundPixel(top)
|
|
690
747
|
};
|
|
748
|
+
// Add CSS variables for arrow positioning
|
|
749
|
+
if (cssArrowOffsetX) {
|
|
750
|
+
style['--semi-tooltip-arrow-offset-x'] = cssArrowOffsetX;
|
|
751
|
+
}
|
|
752
|
+
if (cssArrowOffsetY) {
|
|
753
|
+
style['--semi-tooltip-arrow-offset-y'] = cssArrowOffsetY;
|
|
754
|
+
}
|
|
691
755
|
let transform = '';
|
|
692
756
|
if (translateX != null) {
|
|
693
757
|
transform += `translateX(${translateX * 100}%) `;
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
color: rgba(var(--semi-grey-7), 1);
|
|
92
92
|
}
|
|
93
93
|
.semi-tooltip-wrapper[x-placement=top] .semi-tooltip-icon-arrow {
|
|
94
|
-
left: 50
|
|
94
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
95
95
|
transform: translateX(-50%);
|
|
96
96
|
bottom: -6px;
|
|
97
97
|
}
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
width: 7px;
|
|
130
130
|
height: 24px;
|
|
131
131
|
right: -6px;
|
|
132
|
-
top: 50
|
|
132
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
133
133
|
transform: translateY(-50%);
|
|
134
134
|
}
|
|
135
135
|
.semi-tooltip-wrapper[x-placement=left].semi-tooltip-with-arrow,
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
width: 7px;
|
|
162
162
|
height: 24px;
|
|
163
163
|
left: -6px;
|
|
164
|
-
top: 50
|
|
164
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
165
165
|
transform: translateY(-50%) rotate(180deg);
|
|
166
166
|
}
|
|
167
167
|
.semi-tooltip-wrapper[x-placement=right].semi-tooltip-with-arrow,
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
}
|
|
191
191
|
.semi-tooltip-wrapper[x-placement=bottom] .semi-tooltip-icon-arrow {
|
|
192
192
|
top: -6px;
|
|
193
|
-
left: 50
|
|
193
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
194
194
|
transform: translateX(-50%) rotate(180deg);
|
|
195
195
|
}
|
|
196
196
|
.semi-tooltip-wrapper[x-placement=bottom].semi-tooltip-with-arrow,
|
|
@@ -447,8 +447,11 @@ export default class CascaderFoundation extends BaseFoundation {
|
|
|
447
447
|
} else if (selectedKeys.size && !multiple) {
|
|
448
448
|
inputValue = this.renderDisplayText([...selectedKeys][0]);
|
|
449
449
|
}
|
|
450
|
+
// Reset isSearching immediately in close() instead of relying on afterClose callback
|
|
451
|
+
// This prevents timing issues where open() clears inputValue but isSearching is still true
|
|
450
452
|
this._adapter.updateStates({
|
|
451
|
-
inputValue
|
|
453
|
+
inputValue,
|
|
454
|
+
isSearching: false
|
|
452
455
|
});
|
|
453
456
|
!multiple && this.toggle2SearchInput(false);
|
|
454
457
|
!multiple && this._adapter.updateFocusState(false);
|
|
@@ -484,6 +484,8 @@ export default class FormFoundation extends BaseFoundation {
|
|
|
484
484
|
*/
|
|
485
485
|
const formAllowEmpty = this.getProp('allowEmpty');
|
|
486
486
|
// priority at Field level
|
|
487
|
+
// NOTE: Keep legacy semantics here to avoid implicit breaking changes.
|
|
488
|
+
// (Field-level allowEmpty overriding behavior is handled during register/restore paths.)
|
|
487
489
|
const allowEmpty = fieldAllowEmpty ? fieldAllowEmpty : formAllowEmpty;
|
|
488
490
|
ObjectUtil.set(this.data.values, field, value, allowEmpty);
|
|
489
491
|
/**
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
color: unset;
|
|
77
77
|
}
|
|
78
78
|
.semi-popover-wrapper[x-placement=top] .semi-popover-icon-arrow {
|
|
79
|
-
left: 50
|
|
79
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
80
80
|
transform: translateX(-50%);
|
|
81
81
|
bottom: -7px;
|
|
82
82
|
}
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
width: 8px;
|
|
115
115
|
height: 24px;
|
|
116
116
|
right: -7px;
|
|
117
|
-
top: 50
|
|
117
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
118
118
|
transform: translateY(-50%);
|
|
119
119
|
}
|
|
120
120
|
.semi-popover-wrapper[x-placement=left].semi-popover-with-arrow,
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
width: 8px;
|
|
147
147
|
height: 24px;
|
|
148
148
|
left: -7px;
|
|
149
|
-
top: 50
|
|
149
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
150
150
|
transform: translateY(-50%) rotate(180deg);
|
|
151
151
|
}
|
|
152
152
|
.semi-popover-wrapper[x-placement=right].semi-popover-with-arrow,
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
}
|
|
176
176
|
.semi-popover-wrapper[x-placement=bottom] .semi-popover-icon-arrow {
|
|
177
177
|
top: -7px;
|
|
178
|
-
left: 50
|
|
178
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
179
179
|
transform: translateX(-50%) rotate(180deg);
|
|
180
180
|
}
|
|
181
181
|
.semi-popover-wrapper[x-placement=bottom].semi-popover-with-arrow,
|
package/lib/es/table/rtl.scss
CHANGED
|
@@ -144,6 +144,27 @@ $module: #{$prefix}-table;
|
|
|
144
144
|
border-left: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
// Fix #441 (RTL): when horizontal scroll is enabled and table is NOT scrolled to the far left,
|
|
149
|
+
// the outer left border can be outside of viewport (the real border is rendered by the last column cell).
|
|
150
|
+
// Draw an overlay left border on the container to keep it visible.
|
|
151
|
+
// When scrolled to the far left, `.semi-table-scroll-position-left` exists and we should not draw it.
|
|
152
|
+
&:not(.#{$module}-scroll-position-left) {
|
|
153
|
+
& > .#{$module}-container {
|
|
154
|
+
&::after {
|
|
155
|
+
content: '';
|
|
156
|
+
position: absolute;
|
|
157
|
+
top: 0;
|
|
158
|
+
left: 0;
|
|
159
|
+
bottom: 0;
|
|
160
|
+
width: $width-table_base_border;
|
|
161
|
+
background-color: $color-table-border-default;
|
|
162
|
+
display: block;
|
|
163
|
+
z-index: $z-table_fixed_column + 2;
|
|
164
|
+
pointer-events: none;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
147
168
|
}
|
|
148
169
|
|
|
149
170
|
&-fixed {
|
package/lib/es/table/table.css
CHANGED
|
@@ -416,6 +416,21 @@
|
|
|
416
416
|
border-right: 1px solid var(--semi-color-border);
|
|
417
417
|
border-bottom: 1px solid var(--semi-color-border);
|
|
418
418
|
}
|
|
419
|
+
.semi-table-bordered:not(.semi-table-scroll-position-right) > .semi-table-container::after {
|
|
420
|
+
content: "";
|
|
421
|
+
position: absolute;
|
|
422
|
+
top: 0;
|
|
423
|
+
right: 0;
|
|
424
|
+
bottom: 0;
|
|
425
|
+
width: 1px;
|
|
426
|
+
background-color: var(--semi-color-border);
|
|
427
|
+
display: block;
|
|
428
|
+
z-index: 103;
|
|
429
|
+
pointer-events: none;
|
|
430
|
+
}
|
|
431
|
+
.semi-table-bordered:not(.semi-table-scroll-position-right) > .semi-table-container > .semi-table-header {
|
|
432
|
+
box-shadow: inset-1px 0 0 0 var(--semi-color-border);
|
|
433
|
+
}
|
|
419
434
|
:where(.semi-table-bordered > .semi-table-container) > .semi-table-body > .semi-table-placeholder {
|
|
420
435
|
border-right: 1px solid var(--semi-color-border);
|
|
421
436
|
}
|
|
@@ -435,6 +450,7 @@
|
|
|
435
450
|
position: sticky;
|
|
436
451
|
left: 0px;
|
|
437
452
|
z-index: 1;
|
|
453
|
+
box-sizing: border-box;
|
|
438
454
|
padding: 16px 12px;
|
|
439
455
|
color: var(--semi-color-text-2);
|
|
440
456
|
font-size: 14px;
|
|
@@ -592,6 +608,18 @@
|
|
|
592
608
|
border-right: 0;
|
|
593
609
|
border-left: 1px solid var(--semi-color-border);
|
|
594
610
|
}
|
|
611
|
+
.semi-table-wrapper-rtl .semi-table-bordered:not(.semi-table-scroll-position-left) > .semi-table-container::after {
|
|
612
|
+
content: "";
|
|
613
|
+
position: absolute;
|
|
614
|
+
top: 0;
|
|
615
|
+
left: 0;
|
|
616
|
+
bottom: 0;
|
|
617
|
+
width: 1px;
|
|
618
|
+
background-color: var(--semi-color-border);
|
|
619
|
+
display: block;
|
|
620
|
+
z-index: 103;
|
|
621
|
+
pointer-events: none;
|
|
622
|
+
}
|
|
595
623
|
.semi-table-wrapper-rtl .semi-table-fixed > .semi-table-tbody > .semi-table-row-expand > .semi-table-row-cell > .semi-table-expand-inner, .semi-table-wrapper-rtl .semi-table-fixed > .semi-table-tbody > .semi-table-row-section > .semi-table-row-cell > .semi-table-section-inner {
|
|
596
624
|
left: auto;
|
|
597
625
|
right: 0;
|
package/lib/es/table/table.scss
CHANGED
|
@@ -555,6 +555,35 @@ $module: #{$prefix}-table;
|
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
// Fix #441: when horizontal scroll is enabled (e.g. scroll.x='101%') and table is NOT scrolled to the far right,
|
|
559
|
+
// the outer right border can be outside of viewport because the real right border is rendered by the
|
|
560
|
+
// last column cell border (which is scrollable). Draw an overlay right border on the container to keep it visible.
|
|
561
|
+
// When scrolled to the far right, `.semi-table-scroll-position-right` exists and we should not draw it to avoid
|
|
562
|
+
// double borders.
|
|
563
|
+
&:not(.#{$module}-scroll-position-right) {
|
|
564
|
+
& > .#{$module}-container {
|
|
565
|
+
&::after {
|
|
566
|
+
content: '';
|
|
567
|
+
position: absolute;
|
|
568
|
+
top: 0;
|
|
569
|
+
right: 0;
|
|
570
|
+
bottom: 0;
|
|
571
|
+
width: $width-table_base_border;
|
|
572
|
+
background-color: $color-table-border-default;
|
|
573
|
+
display: block;
|
|
574
|
+
// Make sure the overlay border stays above table content/fixed columns
|
|
575
|
+
z-index: $z-table_fixed_column + 2;
|
|
576
|
+
pointer-events: none;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Ensure header shows a visible right border even when the container overlay
|
|
580
|
+
// is covered by native scrollbars in some browsers.
|
|
581
|
+
& > .#{$module}-header {
|
|
582
|
+
box-shadow: inset -$width-table_base_border 0 0 0 $color-table-border-default;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
558
587
|
:where(& > .#{$module}-container) {
|
|
559
588
|
& > .#{$module}-body > .#{$module}-placeholder {
|
|
560
589
|
border-right: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
|
|
@@ -589,6 +618,9 @@ $module: #{$prefix}-table;
|
|
|
589
618
|
position: sticky;
|
|
590
619
|
left: 0px;
|
|
591
620
|
z-index: 1;
|
|
621
|
+
// In bordered mode, placeholder may receive an extra side border to complete the outer frame.
|
|
622
|
+
// Use border-box to avoid 1px horizontal overflow (and unwanted horizontal scrollbar) in empty data + scroll.y cases.
|
|
623
|
+
box-sizing: border-box;
|
|
592
624
|
padding: #{$spacing-table-paddingY} #{$spacing-table-paddingX};
|
|
593
625
|
color: $color-table_placeholder-text-default;
|
|
594
626
|
font-size: #{$font-table_base-fontSize};
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
&[x-placement='top'] {
|
|
25
25
|
.#{$module-icon} {
|
|
26
|
-
left: 50
|
|
26
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
27
27
|
transform: translateX(-50%);
|
|
28
28
|
bottom: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
|
|
29
29
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
width: $width-tooltip_arrow_vertical;
|
|
62
62
|
height: $height-tooltip_arrow_vertical;
|
|
63
63
|
right: (-$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x);
|
|
64
|
-
top: 50
|
|
64
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
65
65
|
transform: translateY(-50%);
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
width: $width-tooltip_arrow_vertical;
|
|
94
94
|
height: $height-tooltip_arrow_vertical;
|
|
95
95
|
left: -$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x;
|
|
96
|
-
top: 50
|
|
96
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
97
97
|
transform: translateY(-50%) rotate(180deg);
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
&[x-placement='bottom'] {
|
|
123
123
|
.#{$module-icon} {
|
|
124
124
|
top: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
|
|
125
|
-
left: 50
|
|
125
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
126
126
|
transform: translateX(-50%) rotate(180deg);
|
|
127
127
|
}
|
|
128
128
|
|
|
@@ -676,11 +676,75 @@ export default class Tooltip extends BaseFoundation {
|
|
|
676
676
|
top = position.includes('Top') ? top + offsetY : top - offsetY;
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
|
+
// Handle arrowPointAtCenter for center positions (top/bottom/left/right)
|
|
680
|
+
// For center positions, the arrow needs to point at trigger center, not Popover center
|
|
681
|
+
let cssArrowOffsetX;
|
|
682
|
+
let cssArrowOffsetY;
|
|
683
|
+
if (showArrow) {
|
|
684
|
+
const isCenterPosition = ['top', 'bottom', 'left', 'right'].includes(position);
|
|
685
|
+
if (isCenterPosition) {
|
|
686
|
+
if (arrowPointAtCenter) {
|
|
687
|
+
// arrowPointAtCenter=true: arrow should point at trigger center
|
|
688
|
+
// Calculate arrow position relative to Popover left/top edge
|
|
689
|
+
if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
|
|
690
|
+
// Popover center is at `left`, trigger center is at `middleX`
|
|
691
|
+
// Arrow position from Popover left edge:
|
|
692
|
+
// = middleX - (left - wrapperRect.width/2)
|
|
693
|
+
// = middleX - left + wrapperRect.width/2
|
|
694
|
+
// Percentage: (middleX - left) / wrapperRect.width + 0.5
|
|
695
|
+
const arrowOffsetPercent = (middleX - left) / wrapperRect.width + 0.5;
|
|
696
|
+
// Clamp to valid range to prevent arrow going outside Popover
|
|
697
|
+
const minOffset = (horizontalArrowWidth / 2 + positionOffsetX) / wrapperRect.width;
|
|
698
|
+
const maxOffset = 1 - minOffset;
|
|
699
|
+
const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
|
|
700
|
+
// Only set CSS variable if different from default 50%
|
|
701
|
+
if (Math.abs(clampedOffset - 0.5) > 0.01) {
|
|
702
|
+
cssArrowOffsetX = `${clampedOffset * 100}%`;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
|
|
706
|
+
const arrowOffsetPercent = (middleY - top) / wrapperRect.height + 0.5;
|
|
707
|
+
const minOffset = (verticalArrowHeight / 2 + positionOffsetY) / wrapperRect.height;
|
|
708
|
+
const maxOffset = 1 - minOffset;
|
|
709
|
+
const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
|
|
710
|
+
if (Math.abs(clampedOffset - 0.5) > 0.01) {
|
|
711
|
+
cssArrowOffsetY = `${clampedOffset * 100}%`;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
} else {
|
|
715
|
+
// arrowPointAtCenter=false: arrow should be at Popover edge
|
|
716
|
+
// Determine which edge based on trigger position in viewport
|
|
717
|
+
if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
|
|
718
|
+
const offsetXWithArrow = positionOffsetX + horizontalArrowWidth / 2;
|
|
719
|
+
if (isTriggerNearLeft) {
|
|
720
|
+
cssArrowOffsetX = `${offsetXWithArrow / wrapperRect.width * 100}%`;
|
|
721
|
+
} else {
|
|
722
|
+
cssArrowOffsetX = `${(wrapperRect.width - offsetXWithArrow) / wrapperRect.width * 100}%`;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
|
|
726
|
+
const offsetYWithArrow = positionOffsetY + verticalArrowHeight / 2;
|
|
727
|
+
if (isTriggerNearTop) {
|
|
728
|
+
cssArrowOffsetY = `${offsetYWithArrow / wrapperRect.height * 100}%`;
|
|
729
|
+
} else {
|
|
730
|
+
cssArrowOffsetY = `${(wrapperRect.height - offsetYWithArrow) / wrapperRect.height * 100}%`;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
679
736
|
// The left/top value here must be rounded, otherwise it will cause the small triangle to shake
|
|
680
737
|
const style = {
|
|
681
738
|
left: this._roundPixel(left),
|
|
682
739
|
top: this._roundPixel(top)
|
|
683
740
|
};
|
|
741
|
+
// Add CSS variables for arrow positioning
|
|
742
|
+
if (cssArrowOffsetX) {
|
|
743
|
+
style['--semi-tooltip-arrow-offset-x'] = cssArrowOffsetX;
|
|
744
|
+
}
|
|
745
|
+
if (cssArrowOffsetY) {
|
|
746
|
+
style['--semi-tooltip-arrow-offset-y'] = cssArrowOffsetY;
|
|
747
|
+
}
|
|
684
748
|
let transform = '';
|
|
685
749
|
if (translateX != null) {
|
|
686
750
|
transform += `translateX(${translateX * 100}%) `;
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
color: rgba(var(--semi-grey-7), 1);
|
|
92
92
|
}
|
|
93
93
|
.semi-tooltip-wrapper[x-placement=top] .semi-tooltip-icon-arrow {
|
|
94
|
-
left: 50
|
|
94
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
95
95
|
transform: translateX(-50%);
|
|
96
96
|
bottom: -6px;
|
|
97
97
|
}
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
width: 7px;
|
|
130
130
|
height: 24px;
|
|
131
131
|
right: -6px;
|
|
132
|
-
top: 50
|
|
132
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
133
133
|
transform: translateY(-50%);
|
|
134
134
|
}
|
|
135
135
|
.semi-tooltip-wrapper[x-placement=left].semi-tooltip-with-arrow,
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
width: 7px;
|
|
162
162
|
height: 24px;
|
|
163
163
|
left: -6px;
|
|
164
|
-
top: 50
|
|
164
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
165
165
|
transform: translateY(-50%) rotate(180deg);
|
|
166
166
|
}
|
|
167
167
|
.semi-tooltip-wrapper[x-placement=right].semi-tooltip-with-arrow,
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
}
|
|
191
191
|
.semi-tooltip-wrapper[x-placement=bottom] .semi-tooltip-icon-arrow {
|
|
192
192
|
top: -6px;
|
|
193
|
-
left: 50
|
|
193
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
194
194
|
transform: translateX(-50%) rotate(180deg);
|
|
195
195
|
}
|
|
196
196
|
.semi-tooltip-wrapper[x-placement=bottom].semi-tooltip-with-arrow,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@douyinfe/semi-foundation",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.99.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rimraf lib",
|
|
@@ -14225,8 +14225,8 @@
|
|
|
14225
14225
|
}
|
|
14226
14226
|
},
|
|
14227
14227
|
"dependencies": {
|
|
14228
|
-
"@douyinfe/semi-animation": "2.
|
|
14229
|
-
"@douyinfe/semi-json-viewer-core": "2.
|
|
14228
|
+
"@douyinfe/semi-animation": "2.99.0",
|
|
14229
|
+
"@douyinfe/semi-json-viewer-core": "2.99.0",
|
|
14230
14230
|
"@mdx-js/mdx": "^3.0.1",
|
|
14231
14231
|
"async-validator": "^3.5.0",
|
|
14232
14232
|
"classnames": "^2.2.6",
|
|
@@ -14247,7 +14247,7 @@
|
|
|
14247
14247
|
"*.scss",
|
|
14248
14248
|
"*.css"
|
|
14249
14249
|
],
|
|
14250
|
-
"gitHead": "
|
|
14250
|
+
"gitHead": "c62861b574b51ae39afe7da796ab4f14d1462ef2",
|
|
14251
14251
|
"devDependencies": {
|
|
14252
14252
|
"@babel/plugin-transform-runtime": "^7.15.8",
|
|
14253
14253
|
"@babel/preset-env": "^7.15.8",
|
package/table/rtl.scss
CHANGED
|
@@ -144,6 +144,27 @@ $module: #{$prefix}-table;
|
|
|
144
144
|
border-left: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
// Fix #441 (RTL): when horizontal scroll is enabled and table is NOT scrolled to the far left,
|
|
149
|
+
// the outer left border can be outside of viewport (the real border is rendered by the last column cell).
|
|
150
|
+
// Draw an overlay left border on the container to keep it visible.
|
|
151
|
+
// When scrolled to the far left, `.semi-table-scroll-position-left` exists and we should not draw it.
|
|
152
|
+
&:not(.#{$module}-scroll-position-left) {
|
|
153
|
+
& > .#{$module}-container {
|
|
154
|
+
&::after {
|
|
155
|
+
content: '';
|
|
156
|
+
position: absolute;
|
|
157
|
+
top: 0;
|
|
158
|
+
left: 0;
|
|
159
|
+
bottom: 0;
|
|
160
|
+
width: $width-table_base_border;
|
|
161
|
+
background-color: $color-table-border-default;
|
|
162
|
+
display: block;
|
|
163
|
+
z-index: $z-table_fixed_column + 2;
|
|
164
|
+
pointer-events: none;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
147
168
|
}
|
|
148
169
|
|
|
149
170
|
&-fixed {
|
package/table/table.scss
CHANGED
|
@@ -555,6 +555,35 @@ $module: #{$prefix}-table;
|
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
// Fix #441: when horizontal scroll is enabled (e.g. scroll.x='101%') and table is NOT scrolled to the far right,
|
|
559
|
+
// the outer right border can be outside of viewport because the real right border is rendered by the
|
|
560
|
+
// last column cell border (which is scrollable). Draw an overlay right border on the container to keep it visible.
|
|
561
|
+
// When scrolled to the far right, `.semi-table-scroll-position-right` exists and we should not draw it to avoid
|
|
562
|
+
// double borders.
|
|
563
|
+
&:not(.#{$module}-scroll-position-right) {
|
|
564
|
+
& > .#{$module}-container {
|
|
565
|
+
&::after {
|
|
566
|
+
content: '';
|
|
567
|
+
position: absolute;
|
|
568
|
+
top: 0;
|
|
569
|
+
right: 0;
|
|
570
|
+
bottom: 0;
|
|
571
|
+
width: $width-table_base_border;
|
|
572
|
+
background-color: $color-table-border-default;
|
|
573
|
+
display: block;
|
|
574
|
+
// Make sure the overlay border stays above table content/fixed columns
|
|
575
|
+
z-index: $z-table_fixed_column + 2;
|
|
576
|
+
pointer-events: none;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Ensure header shows a visible right border even when the container overlay
|
|
580
|
+
// is covered by native scrollbars in some browsers.
|
|
581
|
+
& > .#{$module}-header {
|
|
582
|
+
box-shadow: inset -$width-table_base_border 0 0 0 $color-table-border-default;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
558
587
|
:where(& > .#{$module}-container) {
|
|
559
588
|
& > .#{$module}-body > .#{$module}-placeholder {
|
|
560
589
|
border-right: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
|
|
@@ -589,6 +618,9 @@ $module: #{$prefix}-table;
|
|
|
589
618
|
position: sticky;
|
|
590
619
|
left: 0px;
|
|
591
620
|
z-index: 1;
|
|
621
|
+
// In bordered mode, placeholder may receive an extra side border to complete the outer frame.
|
|
622
|
+
// Use border-box to avoid 1px horizontal overflow (and unwanted horizontal scrollbar) in empty data + scroll.y cases.
|
|
623
|
+
box-sizing: border-box;
|
|
592
624
|
padding: #{$spacing-table-paddingY} #{$spacing-table-paddingX};
|
|
593
625
|
color: $color-table_placeholder-text-default;
|
|
594
626
|
font-size: #{$font-table_base-fontSize};
|
package/tooltip/arrow.scss
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
&[x-placement='top'] {
|
|
25
25
|
.#{$module-icon} {
|
|
26
|
-
left: 50
|
|
26
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
27
27
|
transform: translateX(-50%);
|
|
28
28
|
bottom: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
|
|
29
29
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
width: $width-tooltip_arrow_vertical;
|
|
62
62
|
height: $height-tooltip_arrow_vertical;
|
|
63
63
|
right: (-$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x);
|
|
64
|
-
top: 50
|
|
64
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
65
65
|
transform: translateY(-50%);
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
width: $width-tooltip_arrow_vertical;
|
|
94
94
|
height: $height-tooltip_arrow_vertical;
|
|
95
95
|
left: -$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x;
|
|
96
|
-
top: 50
|
|
96
|
+
top: var(--semi-tooltip-arrow-offset-y, 50%);
|
|
97
97
|
transform: translateY(-50%) rotate(180deg);
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
&[x-placement='bottom'] {
|
|
123
123
|
.#{$module-icon} {
|
|
124
124
|
top: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
|
|
125
|
-
left: 50
|
|
125
|
+
left: var(--semi-tooltip-arrow-offset-x, 50%);
|
|
126
126
|
transform: translateX(-50%) rotate(180deg);
|
|
127
127
|
}
|
|
128
128
|
|
package/tooltip/foundation.ts
CHANGED
|
@@ -674,12 +674,90 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
|
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
|
|
677
|
+
// Handle arrowPointAtCenter for center positions (top/bottom/left/right)
|
|
678
|
+
// For center positions, the arrow needs to point at trigger center, not Popover center
|
|
679
|
+
let cssArrowOffsetX: string | undefined;
|
|
680
|
+
let cssArrowOffsetY: string | undefined;
|
|
681
|
+
|
|
682
|
+
if (showArrow) {
|
|
683
|
+
const isCenterPosition = ['top', 'bottom', 'left', 'right'].includes(position);
|
|
684
|
+
|
|
685
|
+
if (isCenterPosition) {
|
|
686
|
+
if (arrowPointAtCenter) {
|
|
687
|
+
// arrowPointAtCenter=true: arrow should point at trigger center
|
|
688
|
+
// Calculate arrow position relative to Popover left/top edge
|
|
689
|
+
|
|
690
|
+
if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
|
|
691
|
+
// Popover center is at `left`, trigger center is at `middleX`
|
|
692
|
+
// Arrow position from Popover left edge:
|
|
693
|
+
// = middleX - (left - wrapperRect.width/2)
|
|
694
|
+
// = middleX - left + wrapperRect.width/2
|
|
695
|
+
// Percentage: (middleX - left) / wrapperRect.width + 0.5
|
|
696
|
+
const arrowOffsetPercent = (middleX - left) / wrapperRect.width + 0.5;
|
|
697
|
+
|
|
698
|
+
// Clamp to valid range to prevent arrow going outside Popover
|
|
699
|
+
const minOffset = (horizontalArrowWidth / 2 + positionOffsetX) / wrapperRect.width;
|
|
700
|
+
const maxOffset = 1 - minOffset;
|
|
701
|
+
const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
|
|
702
|
+
|
|
703
|
+
// Only set CSS variable if different from default 50%
|
|
704
|
+
if (Math.abs(clampedOffset - 0.5) > 0.01) {
|
|
705
|
+
cssArrowOffsetX = `${clampedOffset * 100}%`;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
|
|
710
|
+
const arrowOffsetPercent = (middleY - top) / wrapperRect.height + 0.5;
|
|
711
|
+
|
|
712
|
+
const minOffset = (verticalArrowHeight / 2 + positionOffsetY) / wrapperRect.height;
|
|
713
|
+
const maxOffset = 1 - minOffset;
|
|
714
|
+
const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
|
|
715
|
+
|
|
716
|
+
if (Math.abs(clampedOffset - 0.5) > 0.01) {
|
|
717
|
+
cssArrowOffsetY = `${clampedOffset * 100}%`;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
} else {
|
|
721
|
+
// arrowPointAtCenter=false: arrow should be at Popover edge
|
|
722
|
+
// Determine which edge based on trigger position in viewport
|
|
723
|
+
|
|
724
|
+
if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
|
|
725
|
+
const offsetXWithArrow = positionOffsetX + horizontalArrowWidth / 2;
|
|
726
|
+
|
|
727
|
+
if (isTriggerNearLeft) {
|
|
728
|
+
cssArrowOffsetX = `${(offsetXWithArrow / wrapperRect.width) * 100}%`;
|
|
729
|
+
} else {
|
|
730
|
+
cssArrowOffsetX = `${((wrapperRect.width - offsetXWithArrow) / wrapperRect.width) * 100}%`;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
|
|
735
|
+
const offsetYWithArrow = positionOffsetY + verticalArrowHeight / 2;
|
|
736
|
+
|
|
737
|
+
if (isTriggerNearTop) {
|
|
738
|
+
cssArrowOffsetY = `${(offsetYWithArrow / wrapperRect.height) * 100}%`;
|
|
739
|
+
} else {
|
|
740
|
+
cssArrowOffsetY = `${((wrapperRect.height - offsetYWithArrow) / wrapperRect.height) * 100}%`;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
677
747
|
// The left/top value here must be rounded, otherwise it will cause the small triangle to shake
|
|
678
748
|
const style: Record<string, string | number> = {
|
|
679
749
|
left: this._roundPixel(left),
|
|
680
750
|
top: this._roundPixel(top),
|
|
681
751
|
};
|
|
682
752
|
|
|
753
|
+
// Add CSS variables for arrow positioning
|
|
754
|
+
if (cssArrowOffsetX) {
|
|
755
|
+
style['--semi-tooltip-arrow-offset-x'] = cssArrowOffsetX;
|
|
756
|
+
}
|
|
757
|
+
if (cssArrowOffsetY) {
|
|
758
|
+
style['--semi-tooltip-arrow-offset-y'] = cssArrowOffsetY;
|
|
759
|
+
}
|
|
760
|
+
|
|
683
761
|
let transform = '';
|
|
684
762
|
|
|
685
763
|
if (translateX != null) {
|