From 36fe15ddff1177ff8128dc123b28b994917f42b7 Mon Sep 17 00:00:00 2001 From: Brian Ingles Date: Thu, 18 Apr 2024 16:29:03 -0500 Subject: [PATCH] Section heights in scroll position (#1935) --- .../code-studio/src/styleguide/Pickers.tsx | 18 ++++++- packages/code-studio/src/styleguide/utils.ts | 33 +++++++++++- .../components/src/spectrum/picker/Picker.tsx | 5 +- .../src/spectrum/utils/itemUtils.ts | 50 +++++++++++++++---- packages/utils/src/UIConstants.ts | 4 ++ 5 files changed, 95 insertions(+), 15 deletions(-) diff --git a/packages/code-studio/src/styleguide/Pickers.tsx b/packages/code-studio/src/styleguide/Pickers.tsx index bf8183fccf..a266cc8c6b 100644 --- a/packages/code-studio/src/styleguide/Pickers.tsx +++ b/packages/code-studio/src/styleguide/Pickers.tsx @@ -13,10 +13,19 @@ import { Icon } from '@adobe/react-spectrum'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { getPositionOfSelectedItem } from '@deephaven/react-hooks'; import { PICKER_ITEM_HEIGHTS, PICKER_TOP_OFFSET } from '@deephaven/utils'; -import { generateNormalizedItems, sampleSectionIdAndClasses } from './utils'; +import { + generateItemElements, + generateNormalizedItems, + sampleSectionIdAndClasses, +} from './utils'; // Generate enough items to require scrolling const items = [...generateNormalizedItems(52)]; +const itemElementsA = [...generateItemElements(0, 51)]; +const itemElementsB = [...generateItemElements(52, 103)]; +const itemElementsC = [...generateItemElements(104, 155)]; +const itemElementsD = [...generateItemElements(156, 207)]; +const itemElementsE = [...generateItemElements(208, 259)]; function PersonIcon(): JSX.Element { return ( @@ -79,7 +88,7 @@ export function Pickers(): JSX.Element { {'String 1'} {'String 2'} {'String 3'} -
+
Item Aaa Item Bbb @@ -105,6 +114,11 @@ export function Pickers(): JSX.Element { Description that causes overflow
+
{itemElementsA}
+
{itemElementsB}
+
{itemElementsC}
+
{itemElementsD}
+
{itemElementsE}
{ + const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + const len = letters.length; + + for (let i = start; i <= end; i += 1) { + const charI = i % len; + let suffix = String(Math.floor(i / len)); + if (suffix === '0') { + suffix = ''; + } + const letter = letters[charI]; + const key = `${letter}${suffix}`; + const content = `${letter}${letter}${letter}${suffix}`; + + // eslint-disable-next-line react/no-children-prop + yield createElement(Item, { + key, + textValue: content, + children: content, + }); + } +} + /** * Generate a given number of NormalizedItems. * @param count The number of items to generate diff --git a/packages/components/src/spectrum/picker/Picker.tsx b/packages/components/src/spectrum/picker/Picker.tsx index 15538abda7..2adcf6448b 100644 --- a/packages/components/src/spectrum/picker/Picker.tsx +++ b/packages/components/src/spectrum/picker/Picker.tsx @@ -5,6 +5,7 @@ import cl from 'classnames'; import { EMPTY_FUNCTION, PICKER_ITEM_HEIGHTS, + PICKER_SECTION_HEIGHTS, PICKER_TOP_OFFSET, } from '@deephaven/utils'; import { @@ -92,9 +93,11 @@ export function Picker({ const getInitialScrollPosition = useCallback( async () => getPositionOfSelectedItemElement({ - children: wrappedItems, + itemsOrSections: wrappedItems, itemHeight: PICKER_ITEM_HEIGHTS.noDescription, itemHeightWithDescription: PICKER_ITEM_HEIGHTS.withDescription, + sectionTitleHeight: PICKER_SECTION_HEIGHTS.title, + sectionEmptyTitleHeight: PICKER_SECTION_HEIGHTS.emptyTitle, selectedKey: isUncontrolled ? uncontrolledSelectedKey : selectedKey, topOffset: PICKER_TOP_OFFSET, }), diff --git a/packages/components/src/spectrum/utils/itemUtils.ts b/packages/components/src/spectrum/utils/itemUtils.ts index de28d82dc9..c1c0bb2fb3 100644 --- a/packages/components/src/spectrum/utils/itemUtils.ts +++ b/packages/components/src/spectrum/utils/itemUtils.ts @@ -101,37 +101,67 @@ export function getItemKey< return (item?.item?.key ?? item?.key) as TKey; } +/** + * Get the position of the item with the given selected key in a list of items. + */ export async function getPositionOfSelectedItemElement< TKey extends string | number | boolean | undefined, >({ - children, + itemsOrSections, itemHeight, itemHeightWithDescription, + sectionTitleHeight, + sectionEmptyTitleHeight, selectedKey, topOffset, }: { - children: (ItemElement | SectionElement)[]; + itemsOrSections: (ItemElement | SectionElement)[]; selectedKey: TKey | null | undefined; itemHeight: number; itemHeightWithDescription: number; + sectionTitleHeight: number; + sectionEmptyTitleHeight: number; topOffset: number; }): Promise { - let position = 0; + let position = topOffset; + + if (selectedKey == null) { + return position; + } + + const getItemHeight = (item: ItemElement) => + isItemElementWithDescription(item) ? itemHeightWithDescription : itemHeight; // eslint-disable-next-line no-restricted-syntax - for (const child of children) { - if (child.key === selectedKey) { - break; + for (const itemOrSection of itemsOrSections) { + if (itemOrSection.key === selectedKey) { + return position; } - if (isItemElementWithDescription(child)) { - position += itemHeightWithDescription; + if (isSectionElement(itemOrSection)) { + position += + (itemOrSection.props.title ?? '') === '' + ? sectionEmptyTitleHeight + : sectionTitleHeight; + + const childItems = Array.isArray(itemOrSection.props.children) + ? itemOrSection.props.children + : [itemOrSection.props.children]; + + // eslint-disable-next-line no-restricted-syntax + for (const childItem of childItems) { + if (childItem.key === selectedKey) { + return position; + } + + position += getItemHeight(childItem); + } } else { - position += itemHeight; + position += getItemHeight(itemOrSection); } } - return position + topOffset; + return topOffset; } /** diff --git a/packages/utils/src/UIConstants.ts b/packages/utils/src/UIConstants.ts index 4286849996..fcaf459c4e 100644 --- a/packages/utils/src/UIConstants.ts +++ b/packages/utils/src/UIConstants.ts @@ -6,6 +6,10 @@ export const PICKER_ITEM_HEIGHTS = { noDescription: 32, withDescription: 48, } as const; +export const PICKER_SECTION_HEIGHTS = { + title: 33, + emptyTitle: 5, +}; export const PICKER_TOP_OFFSET = 4; export const TABLE_ROW_HEIGHT = 33; export const SCROLL_DEBOUNCE_MS = 150;