UNPKG

@talend/react-components

Version:
331 lines (328 loc) 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = require("react"); var _reactAutowhatever = _interopRequireDefault(require("react-autowhatever")); var _reactI18next = require("react-i18next"); var _reactPopper = require("react-popper"); var _classnames = _interopRequireDefault(require("classnames")); var _propTypes = _interopRequireDefault(require("prop-types")); var _utils = require("@talend/utils"); var _Actions = require("../Actions"); var _constants = _interopRequireDefault(require("../constants")); var _TypeaheadComponent = require("./Typeahead.component.renderers"); var _TypeaheadModule = _interopRequireDefault(require("./Typeahead.module.scss")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function getItems(items, dataFeature) { if (!items) { return []; } if (!dataFeature) { return items; } return items.map(item => ({ 'data-feature': `${dataFeature}.${item.value}`, ...item })); } /** * Show suggestions for search bar * @example * * <Typeahead {...props} /> */ function Typeahead({ onToggle, icon, position, docked, items, ...rest }) { const { t } = (0, _reactI18next.useTranslation)(_constants.default); const [referenceElement, setReferenceElement] = (0, _react.useState)(null); const [popperElement, setPopperElement] = (0, _react.useState)(null); const [focus, setFocus] = (0, _react.useState)(false); const withSameWidth = (0, _react.useMemo)(() => ({ name: 'withSameWidth', enabled: true, phase: 'beforeWrite', requires: ['computeStyles'], fn: ({ state }) => { // eslint-disable-next-line no-param-reassign state.styles.popper.width = `${state.rects.reference.width}px`; }, effect: ({ state }) => { // eslint-disable-next-line no-param-reassign state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`; } }), []); const withInViewport = (0, _react.useMemo)(() => ({ name: 'withInViewport', enabled: true, phase: 'beforeWrite', requires: ['computeStyles'], fn: ({ state }) => { const GAP = 45; // the offset between the end of items container and screen boundaries const inputDimensions = state.rects.reference; const { y, height } = inputDimensions; const offsetTop = y - GAP; const offsetBottom = window.innerHeight - y - height - GAP; const placements = state.placement.split('-'); let newPlacement = state.placement; if (placements[0] === 'top' && offsetBottom > offsetTop) { newPlacement = `bottom-${placements[1]}`; } const maxHeight = newPlacement.includes('top') ? offsetTop : offsetBottom; // eslint-disable-next-line no-param-reassign state.placement = newPlacement; // eslint-disable-next-line no-param-reassign state.styles.popper.maxHeight = `${maxHeight}px`; // eslint-disable-next-line no-param-reassign state.styles.popper.minHeight = `${height}px`; } }), [items]); const withInitialState = (0, _react.useMemo)(() => ({ name: 'withInitialState', enabled: true, phase: 'write', requires: ['computeStyles'], fn: ({ state }) => { if (!focus) { // eslint-disable-next-line no-param-reassign state.styles.popper.opacity = 0; } else { // eslint-disable-next-line no-param-reassign delete state.styles.popper.opacity; } } }), [focus]); const { styles, attributes } = (0, _reactPopper.usePopper)(referenceElement, popperElement, { modifiers: [{ name: 'hide', enabled: false }, { name: 'preventOverflow', enabled: false }, { name: 'computeStyles', options: { adaptive: true } }, withSameWidth, withInViewport, withInitialState], strategy: 'fixed', placement: 'bottom-start' }); const [highlightedSectionIndex, setHighlightedSectionIndex] = (0, _react.useState)(0); const [highlightedItemIndex, setHighlightedItemIndex] = (0, _react.useState)(0); if (docked && onToggle) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Actions.Action, { onClick: onToggle, label: icon.title, hideLabel: true, icon: icon.name, bsStyle: icon.bsStyle, className: (0, _classnames.default)(_TypeaheadModule.default['only-icon-cls'], 'tc-typeahead-toggle'), role: icon.role, tooltipPlacement: icon.tooltipPlacement }); } const handleKeyDown = (event, data) => { switch (event.key) { case 'Down': case 'ArrowDown': case 'Up': case 'ArrowUp': event.preventDefault(); setHighlightedSectionIndex(data.newHighlightedSectionIndex); setHighlightedItemIndex(data.newHighlightedItemIndex); break; case 'Enter': event.preventDefault(); if (highlightedItemIndex !== null && highlightedItemIndex !== null) { rest.onSelect(event, { sectionIndex: data.highlightedSectionIndex, itemIndex: data.highlightedItemIndex }); } break; case 'Esc': case 'Escape': event.preventDefault(); rest.onBlur(event); break; default: } }; const sectionProps = rest.multiSection ? { getSectionItems: section => section.suggestions, renderSectionTitle: _TypeaheadComponent.renderSectionTitle } : null; const themeProps = { theme: { containerOpen: (0, _classnames.default)(_TypeaheadModule.default['container-open'], 'tc-typeahead-container-open'), highlight: (0, _classnames.default)(_TypeaheadModule.default['highlight-match'], 'tc-typeahead-highlight-match'), input: (0, _classnames.default)(_TypeaheadModule.default['typeahead-input'], 'tc-typeahead-typeahead-input'), itemHighlighted: (0, _classnames.default)(_TypeaheadModule.default['item-highlighted'], 'tc-typeahead-item-highlighted'), itemsContainer: (0, _classnames.default)(_TypeaheadModule.default['items-container'], 'tc-typeahead-items-container'), itemsList: _TypeaheadModule.default.items, sectionContainer: (0, _classnames.default)(_TypeaheadModule.default['section-container'], 'tc-typeahead-section-container'), ...rest.theme, container: (0, _classnames.default)(_TypeaheadModule.default['tc-typeahead-container'], 'tc-typeahead-container', position === 'right' && _TypeaheadModule.default.right, rest.theme && rest.theme.container, rest.className, { [_TypeaheadModule.default.loading]: rest.isLoading }) } }; const inputProps = { renderInputComponent: _TypeaheadComponent.renderInputComponent, inputProps: { autoFocus: rest.autoFocus, debounceMinLength: rest.debounceMinLength, debounceTimeout: rest.debounceTimeout, disabled: rest.disabled, id: rest.id, setReferenceElement, onBlur: rest.onBlur, onChange: rest.onChange && (event => rest.onChange(event, { value: event.target.value })), onFocus: event => { if (!focus) { setFocus(true); } if (rest.onFocus) { rest.onFocus(event); } }, onClick: rest.onClick, onKeyDown: rest.onKeyDown || handleKeyDown, placeholder: rest.placeholder, readOnly: rest.readOnly, value: rest.value, icon, caret: rest.caret, role: rest.role, ...rest.inputProps } }; const noResultText = rest.noResultText || t('NO_RESULT_FOUND', { defaultValue: 'No results found' }); const searchingText = rest.searchingText || t('TYPEAHEAD_SEARCHING', { defaultValue: 'Searching for matches...' }); const isLoadingText = rest.isLoadingText || t('TYPEAHEAD_LOADING', { defaultValue: 'Loading...' }); const defaultRenderersProps = { renderItem: _TypeaheadComponent.renderItem, renderItemsContainer: (0, _TypeaheadComponent.renderItemsContainerFactory)(items, noResultText, rest.searching, searchingText, rest.isLoading, isLoadingText, referenceElement, rest.children, setPopperElement, styles, attributes, t, rest.noDomainRenderer), renderItemData: { valueId: rest.valueId, value: rest.value, 'data-feature': rest['data-feature'] } }; const autowhateverProps = { ...defaultRenderersProps, ...rest, ...sectionProps, ...themeProps, ...inputProps, highlightedSectionIndex: rest.onKeyDown ? rest.focusedSectionIndex : highlightedSectionIndex, highlightedItemIndex: rest.onKeyDown ? rest.focusedItemIndex : highlightedItemIndex, items: getItems(items, rest.dataFeature), itemProps: ({ itemIndex }) => ({ onMouseDown: event => event.preventDefault(), onClick: rest.onSelect, 'aria-disabled': items[itemIndex] && items[itemIndex].disabled }) }; return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactAutowhatever.default, { ...autowhateverProps }); } Typeahead.displayName = 'Typeahead'; Typeahead.defaultProps = { autoFocus: false, disabled: false, id: (0, _utils.randomUUID)(), items: null, multiSection: true, // TODO this is for compat, see if we can do the reverse :( position: 'left', readOnly: false, searching: false, docked: false }; Typeahead.propTypes = { // container id: _propTypes.default.string, className: _propTypes.default.string, noResultText: _propTypes.default.string, position: _propTypes.default.oneOf(['left', 'right']), searching: _propTypes.default.bool, searchingText: _propTypes.default.string, // toggle button onToggle: _propTypes.default.func, docked: _propTypes.default.bool, icon: _propTypes.default.shape({ tooltipPlacement: _propTypes.default.string, name: _propTypes.default.string, title: _propTypes.default.string, bsStyle: _propTypes.default.string, role: _propTypes.default.string }), // input autoFocus: _propTypes.default.bool, debounceMinLength: _propTypes.default.number, debounceTimeout: _propTypes.default.number, disabled: _propTypes.default.bool, onBlur: _propTypes.default.func, onChange: _propTypes.default.func, onFocus: _propTypes.default.func, onClick: _propTypes.default.func, placeholder: _propTypes.default.string, readOnly: _propTypes.default.bool, value: _propTypes.default.string, valueId: _propTypes.default.string, // suggestions dataFeature: _propTypes.default.string, onSelect: _propTypes.default.func, onKeyDown: _propTypes.default.func, focusedSectionIndex: _propTypes.default.number, focusedItemIndex: _propTypes.default.number, multiSection: _propTypes.default.bool, items: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.shape({ title: _propTypes.default.string, description: _propTypes.default.string, suggestions: _propTypes.default.arrayOf(_propTypes.default.shape({ title: _propTypes.default.string, description: _propTypes.default.string })) })])), children: _propTypes.default.func // render props }; var _default = exports.default = Typeahead; //# sourceMappingURL=Typeahead.component.js.map