@talend/react-components
Version:
Set of react components.
331 lines (328 loc) • 11.3 kB
JavaScript
"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