diff --git a/app/client/src/components/constants.ts b/app/client/src/components/constants.ts index ec6d1269f171..fb6012da76ec 100644 --- a/app/client/src/components/constants.ts +++ b/app/client/src/components/constants.ts @@ -107,6 +107,13 @@ export enum LabelPosition { */ export const SELECT_DEFAULT_HEIGHT = "32px"; +/** + * Row height passed to rc-select / rc-tree-select virtual lists. Must match dropdown + * option CSS (.rc-select-item, tree treenode). If smaller than real rows, the list + * thinks content fits, omits the scrollbar, and overflow stays hidden. + */ +export const SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX = 38; + /** * Default margin bottom value for old select widgets */ diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx index 2b7b3d990175..13cb7f2e740a 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.styled.tsx @@ -7,7 +7,10 @@ import { LABEL_MARGIN_OLD_SELECT, SELECT_DEFAULT_HEIGHT, } from "components/constants"; -import { CommonSelectFilterStyle } from "widgets/MultiSelectWidgetV2/component/index.styled"; +import { + CommonSelectFilterStyle, + RcVirtualListScrollbarStyles, +} from "widgets/MultiSelectWidgetV2/component/index.styled"; import { Icon, labelLayoutStyles, @@ -277,6 +280,7 @@ border: 1px solid #E8E8E8; outline: none !important; } ${CommonSelectFilterStyle} + ${RcVirtualListScrollbarStyles} .rc-tree-select-item { font-size: 16px; line-height: 1.5; diff --git a/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx b/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx index e5d902c8a651..12e24f51f307 100644 --- a/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx +++ b/app/client/src/widgets/MultiSelectTreeWidget/component/index.tsx @@ -26,7 +26,10 @@ import { Button, Classes, InputGroup } from "@blueprintjs/core"; import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils"; import { Icon } from "@design-system/widgets-old"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import useDropdown from "widgets/useDropdown"; import LabelWithTooltip from "widgets/components/LabelWithTooltip"; @@ -292,6 +295,7 @@ function MultiTreeSelectComponent({ /> } key={key} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} diff --git a/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx index 063a993d0404..4046b8a62f86 100644 --- a/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidget/component/index.styled.tsx @@ -4,6 +4,7 @@ import styled, { keyframes, createGlobalStyle } from "styled-components"; import { Colors } from "constants/Colors"; import type { LabelPosition } from "components/constants"; import { labelLayoutStyles } from "@design-system/widgets-old"; +import { RcVirtualListScrollbarStyles } from "widgets/MultiSelectWidgetV2/component/index.styled"; const rcSelectDropdownSlideUpIn = keyframes` 0% { @@ -222,6 +223,7 @@ ${({ dropDownWidth, id, parentWidth }) => ` margin-bottom: 0; } } + ${RcVirtualListScrollbarStyles} } `; diff --git a/app/client/src/widgets/MultiSelectWidget/component/index.tsx b/app/client/src/widgets/MultiSelectWidget/component/index.tsx index f98604555613..39730468322c 100644 --- a/app/client/src/widgets/MultiSelectWidget/component/index.tsx +++ b/app/client/src/widgets/MultiSelectWidget/component/index.tsx @@ -20,7 +20,10 @@ import { Classes } from "@blueprintjs/core"; import { WidgetContainerDiff } from "widgets/WidgetUtils"; import _ from "lodash"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import LabelWithTooltip from "widgets/components/LabelWithTooltip"; const menuItemSelectedIcon = (props: { isSelected: boolean }) => { @@ -225,6 +228,8 @@ function MultiSelectComponent({ name="dropdown" /> } + listHeight={300} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx index 674850f87970..ff376b28df92 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.styled.tsx @@ -113,6 +113,66 @@ export const CommonSelectFilterStyle = css<{ } `; +/** Thumb size (vertical width / horizontal height). Horizontal track strip is 8px tall. */ +const RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX = 6; +/** Matches vertical track width; list padding uses :has() so short lists don’t reserve a gutter. */ +const RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX = 10; + +const rcVirtualListScrollbarTrackBase = css` + box-sizing: border-box !important; + background-color: #fff !important; +`; + +/** + * rc-virtual-list / rc-tree-select custom scrollbars: white track, emphasis thumb, 36px radius. + * Padding and gutter width stay in sync when a vertical scrollbar is mounted (`:has(...)`). + */ +export const RcVirtualListScrollbarStyles = css` + .rc-virtual-list-holder-inner, + .rc-tree-select-tree-list-holder-inner { + padding-inline-end: 0; + } + .rc-virtual-list:has(.rc-virtual-list-scrollbar-vertical) + .rc-virtual-list-holder-inner, + .rc-tree-select-tree-list:has(.rc-tree-select-tree-list-scrollbar-vertical) + .rc-tree-select-tree-list-holder-inner { + padding-inline-end: ${RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX}px; + } + .rc-virtual-list-scrollbar-vertical, + .rc-tree-select-tree-list-scrollbar-vertical { + ${rcVirtualListScrollbarTrackBase} + width: ${RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX}px !important; + min-width: ${RC_VIRTUAL_LIST_SCROLLBAR_GUTTER_PX}px !important; + } + .rc-virtual-list-scrollbar-horizontal, + .rc-tree-select-tree-list-scrollbar-horizontal { + ${rcVirtualListScrollbarTrackBase} + height: 8px !important; + min-height: 8px !important; + } + .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb, + .rc-tree-select-tree-list-scrollbar + .rc-tree-select-tree-list-scrollbar-thumb { + background-color: var( + --ads-v2-color-bg-emphasis, + rgba(0, 0, 0, 0.35) + ) !important; + border-radius: 36px !important; + } + .rc-virtual-list-scrollbar-vertical .rc-virtual-list-scrollbar-thumb, + .rc-tree-select-tree-list-scrollbar-vertical + .rc-tree-select-tree-list-scrollbar-thumb { + width: ${RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX}px !important; + inset-inline-start: auto !important; + inset-inline-end: 2px !important; + } + .rc-virtual-list-scrollbar-horizontal .rc-virtual-list-scrollbar-thumb, + .rc-tree-select-tree-list-scrollbar-horizontal + .rc-tree-select-tree-list-scrollbar-thumb { + height: ${RC_VIRTUAL_LIST_SCROLLBAR_THUMB_PX}px !important; + } +`; + const Indicator = styled.div` width: 1.2em; height: 1.2em; @@ -381,6 +441,7 @@ ${({ dropDownWidth, id }) => ` margin-bottom: 0; } } + ${RcVirtualListScrollbarStyles} } `; diff --git a/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx b/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx index f29f6d1107bb..d62defc7015c 100644 --- a/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx +++ b/app/client/src/widgets/MultiSelectWidgetV2/component/index.tsx @@ -22,7 +22,10 @@ import type { Alignment } from "@blueprintjs/core"; import { Button, Classes, InputGroup } from "@blueprintjs/core"; import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import { uniqBy } from "lodash"; import { Icon } from "@design-system/widgets-old"; import useDropdown from "widgets/useDropdown"; @@ -361,6 +364,7 @@ function MultiSelectComponent({ } labelInValue listHeight={300} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`} diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx index d318e4c0d1d5..968f177ea0c0 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.styled.tsx @@ -8,7 +8,10 @@ import { LABEL_MARGIN_OLD_SELECT, SELECT_DEFAULT_HEIGHT, } from "components/constants"; -import { CommonSelectFilterStyle } from "widgets/MultiSelectWidgetV2/component/index.styled"; +import { + CommonSelectFilterStyle, + RcVirtualListScrollbarStyles, +} from "widgets/MultiSelectWidgetV2/component/index.styled"; import { Icon, labelLayoutStyles, @@ -243,6 +246,7 @@ ${({ dropDownWidth, id }) => ` outline: none !important; } ${CommonSelectFilterStyle} + ${RcVirtualListScrollbarStyles} .rc-tree-select-item { font-size: 16px; line-height: 1.5; diff --git a/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx b/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx index 57158beac6e1..012f06a0e7ca 100644 --- a/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx +++ b/app/client/src/widgets/SingleSelectTreeWidget/component/index.tsx @@ -25,7 +25,10 @@ import { Button, Classes, InputGroup } from "@blueprintjs/core"; import { labelMargin, WidgetContainerDiff } from "widgets/WidgetUtils"; import { Icon } from "@design-system/widgets-old"; import { Colors } from "constants/Colors"; -import type { LabelPosition } from "components/constants"; +import { + type LabelPosition, + SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX, +} from "components/constants"; import useDropdown from "widgets/useDropdown"; import LabelWithTooltip from "widgets/components/LabelWithTooltip"; import { isNil } from "lodash"; @@ -304,6 +307,7 @@ function SingleSelectTreeComponent({ /> } key={key} + listItemHeight={SELECT_DROPDOWN_LIST_ITEM_HEIGHT_PX} loading={loading} maxTagCount={"responsive"} maxTagPlaceholder={(e) => `+${e.length} more`}