UNPKG

@atlaskit/progress-indicator

Version:

A progress indicator shows the user where they are along the steps of a journey.

145 lines (140 loc) 4.17 kB
import React, { useCallback, useEffect, useRef } from 'react'; import { bind } from 'bind-event-listener'; import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next/usePlatformLeafEventHandler'; import noop from '@atlaskit/ds-lib/noop'; import { Box, Inline } from '@atlaskit/primitives/compiled'; import { ButtonIndicator } from './button-indicator'; import { PresentationalIndicator } from './presentational-indicator'; const packageName = "@atlaskit/progress-indicator"; const packageVersion = "13.4.2"; const progressIndicatorGapMap = { comfortable: { default: 'space.100', large: 'space.150' }, cozy: { default: 'space.075', large: 'space.100' }, compact: { default: 'space.050', large: 'space.075' } }; const sizes = { default: 8, large: 12 }; const varDotsSize = '--ds-dots-size'; const varDotsMargin = '--ds-dots-margin'; /** * __ProgressDots__ * * A progress indicator shows the user where they are along the steps of a journey. */ const ProgressDots = ({ appearance = 'default', ariaControls = 'panel', ariaLabel = 'tab', getAriaLabel, size = 'default', // NOTE: `spacing` is a reserved HTML attribute and will be added to the // element, replaced with `gutter`. spacing: gutter = 'comfortable', selectedIndex, testId, values, onSelect }) => { const tablistRef = useRef(null); const onSelectWithAnalytics = usePlatformLeafEventHandler({ fn: onSelect || noop, action: 'selected', componentName: 'progressIndicator', packageName, packageVersion }); const gap = progressIndicatorGapMap[gutter][size]; const handleKeyDown = useCallback(event => { const indicators = Array.from(tablistRef.current.children); // bail if the target isn't an indicator if (!indicators.includes(event.target)) { return; } // bail if not valid arrow key const isLeft = event.key === 'ArrowLeft'; const isRight = event.key === 'ArrowRight'; if (!isLeft && !isRight) { return; } // bail if at either end of the values const isAlpha = isLeft && selectedIndex === 0; const isOmega = isRight && selectedIndex === values.length - 1; if (isAlpha || isOmega) { return; } const index = isLeft ? selectedIndex - 1 : selectedIndex + 1; // call the consumer's select method and focus the applicable indicator if (onSelect) { onSelectWithAnalytics({ event: event, index }); } if (typeof indicators[index].focus === 'function') { indicators[index].focus(); } }, [onSelectWithAnalytics, selectedIndex, values, onSelect]); useEffect(() => { if (!onSelect) { return noop; } return bind(document, { type: 'keydown', listener: handleKeyDown, options: { capture: false } }); }, [onSelect, handleKeyDown]); return /*#__PURE__*/React.createElement(Box, { style: { [varDotsSize]: `${sizes[size]}px`, // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766 [varDotsMargin]: gap }, role: onSelect && 'tablist' }, /*#__PURE__*/React.createElement(Inline, { testId: testId, ref: r => { tablistRef.current = r; }, alignInline: "center", space: gap }, values.map((_, index) => { const isSelected = selectedIndex === index; const tabId = `${ariaLabel}${index}`; const panelId = `${ariaControls}${index}`; const label = getAriaLabel ? getAriaLabel(index) : tabId; const indicatorTestId = testId && `${testId}-ind-${index}`; return onSelect ? /*#__PURE__*/React.createElement(ButtonIndicator, { key: index, testId: indicatorTestId, appearance: appearance, isSelected: isSelected, tabId: tabId, panelId: panelId, label: label, onClick: event => onSelectWithAnalytics({ event, index }) }) : /*#__PURE__*/React.createElement(PresentationalIndicator, { key: index, testId: indicatorTestId, appearance: appearance, isSelected: isSelected }); }))); }; export default ProgressDots;