diff --git a/src/internal/components/selectable-item/styles.scss b/src/internal/components/selectable-item/styles.scss index a621b07121..b71c4e06a1 100644 --- a/src/internal/components/selectable-item/styles.scss +++ b/src/internal/components/selectable-item/styles.scss @@ -206,6 +206,60 @@ &:first-of-type:not(.selected, .highlighted) { border-block-start-color: awsui.$color-border-dropdown-item-top; } + + // When using virtual scrolling, use box-shadows to mimic changing border + // width and set the actual border to a fixed width to prevent jumping + // behaviour when hovering selectable items + + $virtual-border-offset: calc(#{awsui.$border-item-width} - #{awsui.$border-divider-list-width}); + $virtual-border-offset-double: calc(2 * #{awsui.$border-item-width} - #{awsui.$border-divider-list-width}); + + &.highlighted:not(.selected), + &.selected { + border-width: awsui.$border-divider-list-width; + padding-block: calc(#{styles.$option-padding-vertical} + #{$virtual-border-offset}); + padding-inline: calc(#{styles.$control-padding-horizontal} + #{$virtual-border-offset}); + + &.pad-bottom { + padding-block-end: calc(#{styles.$option-padding-vertical} + #{awsui.$space-xxxs} + #{$virtual-border-offset}); + } + + &.child { + padding-inline-start: calc(#{awsui.$space-xxl} + #{$virtual-border-offset}); + } + + &.parent.interactiveGroups { + padding-block: calc(#{styles.$group-option-padding-vertical} + #{$virtual-border-offset}); + } + } + + &.highlighted:not(.selected) { + box-shadow: inset 0 0 0 $virtual-border-offset awsui.$color-border-dropdown-item-hover; + + &.is-keyboard { + box-shadow: inset 0 0 0 $virtual-border-offset awsui.$color-border-dropdown-item-focused; + } + } + + &.selected { + box-shadow: inset 0 0 0 $virtual-border-offset awsui.$color-border-dropdown-item-selected; + + &.highlighted { + box-shadow: + inset 0 0 0 $virtual-border-offset awsui.$color-border-dropdown-item-selected, + inset 0 0 0 $virtual-border-offset-double awsui.$color-border-dropdown-item-hover; + + &.is-keyboard { + box-shadow: + inset 0 0 0 $virtual-border-offset awsui.$color-border-dropdown-item-selected, + inset 0 0 0 $virtual-border-offset-double awsui.$color-border-dropdown-item-focused; + } + } + } + + &.parent:not(.interactiveGroups) { + border-block-start-color: awsui.$color-border-dropdown-group; + } } } diff --git a/src/select/parts/virtual-list.tsx b/src/select/parts/virtual-list.tsx index 5db64cda75..c6b0e8be62 100644 --- a/src/select/parts/virtual-list.tsx +++ b/src/select/parts/virtual-list.tsx @@ -100,6 +100,10 @@ const VirtualListOpen = forwardRef( withScrollbar, }); + // Adjust totalSize to account for 1px overlap per item (matching the position adjustment in renderOptions) + const overlapAdjustment = filteredOptions.length; + const adjustedTotalSize = totalSize - overlapAdjustment; + return ( {finalOptions} @@ -107,7 +111,7 @@ const VirtualListOpen = forwardRef( aria-hidden="true" key="total-size" className={styles['layout-strut']} - style={{ height: totalSize - stickySize }} + style={{ height: adjustedTotalSize - stickySize }} /> {listBottom ? (
diff --git a/src/select/utils/render-options.tsx b/src/select/utils/render-options.tsx index 614f0d2dd6..580c1a06c9 100644 --- a/src/select/utils/render-options.tsx +++ b/src/select/utils/render-options.tsx @@ -65,11 +65,15 @@ export const renderOptions = ({ const ListItem = useInteractiveGroups ? MultiselectItem : Item; const isSticky = firstOptionSticky && globalIndex === 0; + // Adjust virtual position to create 1px overlap between items (matching non-virtual behavior) + // Subtract globalIndex to shift each item up by 1px per item + const adjustedVirtualPosition = virtualItem ? virtualItem.start - globalIndex : undefined; + return (