diff --git a/cypress/snapshots/ui/components/Pickers/DatePicker.component-test.tsx/plasma-ui DatePicker -- with single item.snap.png b/cypress/snapshots/ui/components/Pickers/DatePicker.component-test.tsx/plasma-ui DatePicker -- with single item.snap.png new file mode 100644 index 000000000..521c72bec Binary files /dev/null and b/cypress/snapshots/ui/components/Pickers/DatePicker.component-test.tsx/plasma-ui DatePicker -- with single item.snap.png differ diff --git a/cypress/snapshots/ui/components/Pickers/TimePicker.component-test.tsx/plasma-ui TimePicker update value -- with single item.snap.png b/cypress/snapshots/ui/components/Pickers/TimePicker.component-test.tsx/plasma-ui TimePicker update value -- with single item.snap.png new file mode 100644 index 000000000..61c6dad77 Binary files /dev/null and b/cypress/snapshots/ui/components/Pickers/TimePicker.component-test.tsx/plasma-ui TimePicker update value -- with single item.snap.png differ diff --git a/packages/plasma-ui/src/components/Pickers/DatePicker.component-test.tsx b/packages/plasma-ui/src/components/Pickers/DatePicker.component-test.tsx index 4bf27ba49..225f020f0 100644 --- a/packages/plasma-ui/src/components/Pickers/DatePicker.component-test.tsx +++ b/packages/plasma-ui/src/components/Pickers/DatePicker.component-test.tsx @@ -133,4 +133,28 @@ describe('plasma-ui: DatePicker', () => { cy.wait(100); cy.matchImageSnapshot(); }); + + it('with single item', () => { + mount( + + <> + + + + , + ); + + cy.matchImageSnapshot(); + }); }); diff --git a/packages/plasma-ui/src/components/Pickers/Picker.tsx b/packages/plasma-ui/src/components/Pickers/Picker.tsx index eb923c24a..11c1b01b5 100644 --- a/packages/plasma-ui/src/components/Pickers/Picker.tsx +++ b/packages/plasma-ui/src/components/Pickers/Picker.tsx @@ -120,6 +120,7 @@ interface StyledWrapperProps { $size: PickerSize; $disabled?: boolean; $controls?: boolean; + $isSingleItem?: boolean; } const StyledWrapper = styled.div` position: relative; @@ -158,6 +159,14 @@ const StyledWrapper = styled.div` padding-top: 1.25rem; padding-bottom: 1.25rem; `} + + ${({ $isSingleItem }) => + $isSingleItem && + css` + ${StyledCarousel} { + overflow: hidden; + } + `} `; // Значение, отвечающее за количество элементов, @@ -176,7 +185,16 @@ function getAllIndices(items: PickerItemType[], value: string | number | Date) { return res; } -const findItemIndex = (items: PickerItemType[], value: string | number | Date, infiniteScroll: boolean) => { +const findItemIndex = ( + items: PickerItemType[], + value: string | number | Date, + infiniteScroll: boolean, + isSingleItem: boolean, +) => { + if (infiniteScroll && isSingleItem) { + return Math.floor(items.length / 2); + } + if (infiniteScroll) { const middleIndex = 1; return getAllIndices(items, value)[middleIndex]; @@ -209,7 +227,16 @@ const getIndex = (index: number, cmd: GetIndexCmd, min: number, max: number, inf } }; -const getItems = (items: PickerItemType[], infiniteScroll: boolean) => { +const getItems = ( + items: PickerItemType[], + infiniteScroll: boolean, + visibleItems: PickerVisibleItems, + isSingleItem: boolean, +) => { + if (infiniteScroll && isSingleItem) { + return new Array(visibleItems).fill(items[0]); + } + if (infiniteScroll) { const virtualItems = items.map((item) => ({ ...item, isVirtual: true })); return [...virtualItems, ...items, ...virtualItems]; @@ -288,34 +315,36 @@ export const Picker: FC = ({ 'aria-label': ariaLabel, ...rest }) => { + const isSingleItem = items.length === 1; + const min = 0; + const max = items.length - 1; + const theme = useContext(ThemeContext); // by default 'true' on high perfomance devices - const infiniteScroll = rest.infiniteScroll ?? !theme.lowPerformance; - - const virtualItems = useMemo(() => getItems(items, infiniteScroll), [items, infiniteScroll]); + const infiniteScroll = rest.infiniteScroll ?? !theme?.lowPerformance; - const min = 0; - const max = items.length - 1; + const virtualItems = useMemo(() => getItems(items, infiniteScroll, visibleItems, isSingleItem), [ + items, + infiniteScroll, + visibleItems, + isSingleItem, + ]); const isFirstRender = useFirstRender(); const [isFocused, setIsFocused] = useState(false); - const [index, setIndex] = useState(findItemIndex(virtualItems, value, infiniteScroll)); + const [index, setIndex] = useState(findItemIndex(virtualItems, value, infiniteScroll, isSingleItem)); const [hasScrollAnim, setScrollAnim] = useState(true); const wrapperRef = useRef(null); const carouselRef = useRef(null); - const toPrev = useCallback(() => !disabled && setIndex(getIndex(index, '-', min, max, infiniteScroll)), [ - index, - min, - max, - infiniteScroll, - ]); - const toNext = useCallback(() => !disabled && setIndex(getIndex(index, '+', min, max, infiniteScroll)), [ - index, - min, - max, - infiniteScroll, - ]); + const toPrev = useCallback( + () => !isSingleItem && !disabled && setIndex(getIndex(index, '-', min, max, infiniteScroll)), + [isSingleItem, index, min, max, infiniteScroll], + ); + const toNext = useCallback( + () => !isSingleItem && !disabled && setIndex(getIndex(index, '+', min, max, infiniteScroll)), + [isSingleItem, index, min, max, infiniteScroll], + ); const jump = useCallback( (cmd: GetIndexCmd) => { if (disabled) { @@ -340,7 +369,7 @@ export const Picker: FC = ({ // Изменяет индекс выделенного элемента // при обновлении значения value извне useIsomorphicLayoutEffect(() => { - const newIndex = findItemIndex(virtualItems, value, infiniteScroll); + const newIndex = findItemIndex(virtualItems, value, infiniteScroll, isSingleItem); // Отключаем анимацию скролла, если значение компонента осталось // прежним, но индекс изменился @@ -360,7 +389,7 @@ export const Picker: FC = ({ } setIndex(newIndex); - }, [value, virtualItems, infiniteScroll, max]); + }, [value, virtualItems, infiniteScroll, max, isSingleItem]); // Навигация с помощью пульта/клавиатуры // Не перелистывает, если компонент неактивен @@ -412,19 +441,19 @@ export const Picker: FC = ({ if (prevValue === virtualItems[i]?.value) { setScrollAnim(false); setIndex(i); - const newIndex = findItemIndex(virtualItems, virtualItems[i].value, infiniteScroll); + const newIndex = findItemIndex(virtualItems, virtualItems[i].value, infiniteScroll, isSingleItem); setIndex(newIndex); } // Включаем анимацию скролла, после изменения индекса setScrollAnim(true); }, - [virtualItems, infiniteScroll, value, onChange, prevValue], + [virtualItems, infiniteScroll, value, onChange, prevValue, isSingleItem], ); const onDetectActiveItem = useCallback( (i: number) => { - if (!infiniteScroll || (!isTopPosition(i) && !isBottomPosition(i, virtualItems.length))) { + if (isSingleItem || !infiniteScroll || (!isTopPosition(i) && !isBottomPosition(i, virtualItems.length))) { return; } @@ -443,7 +472,7 @@ export const Picker: FC = ({ setIndex(i - (max - min) - 1); } }, - [virtualItems, infiniteScroll, max, min, index], + [virtualItems, infiniteScroll, max, min, index, isSingleItem], ); return ( @@ -453,6 +482,7 @@ export const Picker: FC = ({ $size={size} $disabled={disabled} $visibleItems={visibleItems} + $isSingleItem={isSingleItem} $controls={controls} onFocus={onFocus} onBlur={onBlur} diff --git a/packages/plasma-ui/src/components/Pickers/TimePicker.component-test.tsx b/packages/plasma-ui/src/components/Pickers/TimePicker.component-test.tsx index f80498e44..c63d0557e 100644 --- a/packages/plasma-ui/src/components/Pickers/TimePicker.component-test.tsx +++ b/packages/plasma-ui/src/components/Pickers/TimePicker.component-test.tsx @@ -185,4 +185,28 @@ describe('plasma-ui: TimePicker update value', () => { cy.matchImageSnapshot(); }); + + it('with single item', () => { + mount( + + <> + + + + , + ); + + cy.matchImageSnapshot(); + }); });