@retailmenot/anchor
Version:
A React UI Library by RetailMeNot
626 lines (549 loc) • 20.6 kB
JavaScript
import { c as _slicedToArray, e as _typeof, a as _taggedTemplateLiteral } from './anchor-chunk-7b9d8557.js';
import { a as __rest } from './anchor-chunk-27f34e54.js';
import { Input } from './form.js';
import { List } from './list.js';
import { forwardRef, createElement, useContext, useState, Children, Fragment, useEffect, useRef, createRef, Component, cloneElement, useReducer, PureComponent, isValidElement, createContext, useImperativeHandle } from 'react';
import classNames from 'classnames';
import styled, { css } from '@xstyled/styled-components';
import { th, space } from '@xstyled/system';
import './anchor-chunk-5b0bbe0b.js';
import './anchor-chunk-cd7ef49a.js';
import './anchor-chunk-20e4020f.js';
import './anchor-chunk-6ebffda8.js';
import './anchor-chunk-210b63ef.js';
import './addevent.js';
import './anchor-chunk-25c07228.js';
import './arrowback.js';
import './arrowforward.js';
import './avataricon.js';
import './avataroutline.js';
import './barcode.js';
import './bulletlist.js';
import './calendar.js';
import './camera.js';
import './cart.js';
import './cashback.js';
import './cells.js';
import './chat.js';
import './check.js';
import './checksmall.js';
import './chevrondown.js';
import './chevrondownsmall.js';
import './chevronleft.js';
import './chevronleftsmall.js';
import './chevronright.js';
import './chevronrightsmall.js';
import './chevronup.js';
import './chevronupsmall.js';
import './clock.js';
import './close.js';
import './closesmall.js';
import './commentmore.js';
import './creditcard.js';
import './crosshairs.js';
import './cut.js';
import './disabled.js';
import './dislike.js';
import './download.js';
import './ellipses.js';
import './ellipsesvertical.js';
import './envelope.js';
import './envelopeopen.js';
import './error.js';
import './expand.js';
import './gear.js';
import './giftcard.js';
import './hamburger.js';
import './heart.js';
import './heartoutline.js';
import './home.js';
import './info.js';
import './infooutline.js';
import './laptop.js';
import './lightning.js';
import './like.js';
import './listicon.js';
import './lock.js';
import './map.js';
import './marker.js';
import './markeroutline.js';
import './mobile.js';
import './news.js';
import './pencil.js';
import './play.js';
import './plus.js';
import './plussmall.js';
import './print.js';
import './question.js';
import './questionoutline.js';
import './refresh.js';
import './retailmenotlogo.js';
import './sadface.js';
import './search.js';
import './share.js';
import './sliders.js';
import './star.js';
import './starhalf.js';
import './staroutline.js';
import './success.js';
import './successoutline.js';
import './tag.js';
import './upload.js';
import './tagadd.js';
function _templateObject() {
var data = _taggedTemplateLiteral(["\n background-color: white;\n position: absolute;\n width: inherit;\n z-index: 3;\n box-sizing: border-box;\n box-shadow: 0 0.5rem 0.75rem -0.375rem rgba(0, 0, 0, 0.2);\n border-radius: 0 0 ", " ", ";\n padding: 1rem;\n ", ";\n"]);
_templateObject = function _templateObject() {
return data;
};
return data;
}
var useEffect$1 = useEffect,
useState$1 = useState,
forwardRef$1 = forwardRef,
useImperativeHandle$1 = useImperativeHandle;
var ResultContainerSpaceFromAutoComplete = {
sm: '2.6rem',
md: '3.25rem',
lg: '3.25rem'
};
var StyledResultsContainer = styled('div')(_templateObject(), th.radius('base'), th.radius('base'), function (_ref) {
var _ref$size = _ref.size,
size = _ref$size === void 0 ? 'md' : _ref$size;
return css({
top: ResultContainerSpaceFromAutoComplete[size]
});
});
var createResult = function createResult(label, value) {
return {
label: label,
value: value || label
};
};
var generateResults = function generateResults(results, currentIndex, setCurrentIndex, emitSelectedItem, term, setTerm) {
var cleanResults = [];
if (Array.isArray(results) && results.length) {
// Generic arrays are converted to DataItems
if (typeof results[0] === 'string') {
results.forEach(function (result, index) {
var itemIndex = relativeIndex(index);
cleanResults.push(createResult(result, {
label: result || '',
// Add one to index so that the input is an index
active: itemIndex === currentIndex,
key: "anchor-result-".concat(itemIndex),
onMouseOver: function onMouseOver() {
setCurrentIndex(itemIndex);
setTerm(term);
},
onSelect: function onSelect() {
return emitSelectedItem({
label: result,
value: {
label: result,
value: result
}
});
}
}));
});
} else if (_typeof(results[0]) === 'object') {
results.forEach(function (_a, index) {
var _a$label = _a.label,
label = _a$label === void 0 ? '' : _a$label,
props = __rest(_a, ["label"]);
var itemIndex = relativeIndex(index);
cleanResults.push(createResult(label, Object.assign(Object.assign({
label: label
}, props), {
// Add one to index so that the input is an index
active: itemIndex === currentIndex,
key: "anchor-result-".concat(itemIndex),
onMouseOver: function onMouseOver() {
setCurrentIndex(itemIndex);
setTerm(term);
},
onSelect: function onSelect() {
return emitSelectedItem({
label: label,
value: Object.assign({
label: label
}, props)
});
}
})));
});
}
}
return cleanResults;
};
/*
Entire List: [ 0, 1, 2, 3, 4 ]
Iterable List: [ 1, 3, 4 ]
forward:
- go to next iterable index
- unless at max; go to 0 (assign initialTerm)
backward:
- go to previous iterable index
- unless at 0; go to max (assign initialTerm)
both
- store initialTerm
- Emit initialTerm or currentLabel
- Update currentIndex
===================================================
getNext(currentTerm: string, max = iterableList.length - 1, min = 0, currentIndex, direction): number
emitTermAndUpdateIndex(newIndex, values): void
• updateIndex(emitActiveIndex, setCurrentIndex): void
• updateTerm(emitActiveIndex, setCurrentIndex): void
*/
var relativeIndex = function relativeIndex(index) {
return index + 1;
};
var ResultsContainer = forwardRef$1(function (_a, resultsContainerRef) {
var className = _a.className,
_a$dataSource = _a.dataSource,
dataSource = _a$dataSource === void 0 ? [] : _a$dataSource,
emitSelectedItem = _a.emitSelectedItem,
emitActiveTerm = _a.emitActiveTerm,
highlightFirst = _a.highlightFirst,
term = _a.term,
resultTemplate = _a.resultTemplate,
props = __rest(_a, ["className", "dataSource", "emitSelectedItem", "emitActiveTerm", "highlightFirst", "term", "resultTemplate"]);
var _useState = useState$1(0),
_useState2 = _slicedToArray(_useState, 2),
currentIndex = _useState2[0],
setCurrentIndex = _useState2[1];
var _useState3 = useState$1(''),
_useState4 = _slicedToArray(_useState3, 2),
initialTerm = _useState4[0],
setInitialTerm = _useState4[1];
var results = generateResults(dataSource, currentIndex, setCurrentIndex, emitSelectedItem, term, setInitialTerm);
useEffect$1(function () {
// Check if current term exists in results and capture the index
var termIndex;
if (term) {
termIndex = relativeIndex(results.findIndex(function (result) {
return result.label === term;
}));
} // If highlightFirst is true and term does not exist in results
// update termIndex to the first result
if (highlightFirst && !termIndex) {
// If first item is a title, skip to next item
termIndex = results[0].value.listItemType === 'title' ? 2 : 1;
} // Set term index if it exists along with initial term
if (termIndex) {
setCurrentIndex(termIndex);
setInitialTerm(term ? term : results[termIndex - 1].label);
}
}, []);
var iterativeIndexes = [0];
results.forEach(function (_ref2, index) {
var listItemType = _ref2.value.listItemType;
if (!listItemType || listItemType === 'item') {
// Add one to index to allow input to be item 0
iterativeIndexes.push(relativeIndex(index));
}
});
var traverse = function traverse(range, index, forward) {
// Get the max range; the minimum index of any array is 0
var max = range.length - 1; // Determine the actual index relative to all iterable values
var indexRelative = range.indexOf(index); // Increment/decrement accordingly
var nextIndex = forward ? indexRelative + 1 : indexRelative - 1; // For 'next' iteration, reset to 0 if at max
if (forward && nextIndex > max) {
nextIndex = 0;
} // For 'prev' iteration, reset to max if at 0
if (!forward && nextIndex < 0) {
nextIndex = max;
}
return nextIndex;
};
var handleTraversal = function handleTraversal() {
var currentTerm = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var forward = arguments.length > 1 ? arguments[1] : undefined;
// Check if there's a initialTerm
if (!initialTerm || initialTerm === '') {
// If there's no initialTerm, assign the current input value
setInitialTerm(currentTerm);
} // Determine the next index
var nextIndex = traverse(iterativeIndexes, currentIndex, forward); // Assign that index locally
setCurrentIndex(iterativeIndexes[nextIndex]);
if (nextIndex === 0) {
// Emit the user's initial term
emitActiveTerm(initialTerm);
setInitialTerm('');
} else {
// Emit the active term, but subtract one bc the input is not part of the original data set
emitActiveTerm(results[iterativeIndexes[nextIndex] - 1].label);
}
};
var isInRange = currentIndex >= 0 && currentIndex <= results.length; // If currentIndex is 0, it's referring to the input field
var adjustedForInputIndex = currentIndex ? currentIndex - 1 : 0; // Check for a title item index and skip it
var checkForTitleIndex = function checkForTitleIndex(index) {
var shouldBumpIndex = results[index].value.listItemType === 'title' && // Check if title is last result
index < results.length;
return shouldBumpIndex ? index + 1 : index;
};
useImperativeHandle$1(resultsContainerRef, function () {
return {
setActiveIndex: function setActiveIndex(itemIndex) {
setCurrentIndex(highlightFirst ? relativeIndex(checkForTitleIndex(itemIndex)) : itemIndex);
},
clearInitialTerm: function clearInitialTerm() {
setInitialTerm('');
},
handleNext: function handleNext(currentTerm) {
handleTraversal(currentTerm, true);
},
updateTerm: function updateTerm(currentTerm) {
if (isInRange) {
emitActiveTerm(results[checkForTitleIndex(adjustedForInputIndex)].label);
}
},
handlePrevious: function handlePrevious(currentTerm) {
handleTraversal(currentTerm, false);
},
selectActive: function selectActive() {
if (isInRange) {
emitSelectedItem(results[adjustedForInputIndex]);
}
}
};
});
return createElement(StyledResultsContainer, Object.assign({
className: classNames('anchor-auto-complete-results-container', className)
}, props), resultTemplate ? createElement("div", {
className: "auto-complete-results"
}, results.map(function (_ref3, index) {
var label = _ref3.label,
value = _ref3.value;
return createElement(resultTemplate, {
label: label,
value: value,
term: term,
currentIndex: currentIndex,
index: relativeIndex(index),
key: relativeIndex(index)
});
})) : createElement(List, {
items: results,
className: "auto-complete-results"
}));
});
function _templateObject$1() {
var data = _taggedTemplateLiteral(["\n width: 100%;\n position: relative;\n border: solid thin transparent;\n ", ";\n border-radius: base;\n transition: border-color 250ms;\n ", "\n ", ";\n\n &:hover {\n ", ";\n }\n\n &:active,\n &.focus,\n &:focus {\n ", ";\n }\n\n .auto-complete-input {\n border: none;\n }\n ", "\n"]);
_templateObject$1 = function _templateObject() {
return data;
};
return data;
}
var useEffect$2 = useEffect,
useState$2 = useState,
useRef$1 = useRef;
var EventKeyCodes = {
TAB: 9,
ENTER: 13,
ARROW_UP: 38,
ARROW_DOWN: 40
};
var StyledAutoComplete = styled('div')(_templateObject$1(), function (_ref) {
var border = _ref.border;
return css({
borderColor: border ? 'borders.base' : 'transparent'
});
}, function (_ref2) {
var backgroundColor = _ref2.background,
color = _ref2.color;
return css({
backgroundColor: backgroundColor,
color: color
});
}, function (_ref3) {
var shadow = _ref3.shadow;
return shadow ? 'box-shadow: 0 0.5rem 0.75rem -0.375rem rgba(0, 0, 0, 0.12);' : null;
}, function (_ref4) {
var border = _ref4.border;
return css({
borderColor: border ? 'borders.dark' : 'transparent'
});
}, function (_ref5) {
var border = _ref5.border;
return css({
borderColor: border ? 'borders.dark' : 'transparent'
});
}, space);
var AutoComplete = function AutoComplete(_a) {
var allowClear = _a.allowClear,
_a$browserAutoComplet = _a.browserAutoComplete,
browserAutoComplete = _a$browserAutoComplet === void 0 ? false : _a$browserAutoComplet,
_a$autoFocus = _a.autoFocus,
autoFocus = _a$autoFocus === void 0 ? false : _a$autoFocus,
_a$background = _a.background,
background = _a$background === void 0 ? 'white' : _a$background,
_a$border = _a.border,
border = _a$border === void 0 ? true : _a$border,
className = _a.className,
_a$color = _a.color,
color = _a$color === void 0 ? 'text.light' : _a$color,
_a$dataSource = _a.dataSource,
dataSource = _a$dataSource === void 0 ? [] : _a$dataSource,
_a$debug = _a.debug,
debug = _a$debug === void 0 ? false : _a$debug,
_a$highlightFirst = _a.highlightFirst,
highlightFirst = _a$highlightFirst === void 0 ? false : _a$highlightFirst,
inputProps = _a.inputProps,
_a$inputType = _a.inputType,
inputType = _a$inputType === void 0 ? 'text' : _a$inputType,
_a$name = _a.name,
name = _a$name === void 0 ? '' : _a$name,
_a$onChange = _a.onChange,
onChange = _a$onChange === void 0 ? function () {
return null;
} : _a$onChange,
_a$onFilter = _a.onFilter,
onFilter = _a$onFilter === void 0 ? function () {
return null;
} : _a$onFilter,
_a$onSelect = _a.onSelect,
onSelect = _a$onSelect === void 0 ? function () {
return null;
} : _a$onSelect,
placeholder = _a.placeholder,
prefix = _a.prefix,
resultTemplate = _a.resultTemplate,
_a$shadow = _a.shadow,
shadow = _a$shadow === void 0 ? false : _a$shadow,
_a$size = _a.size,
size = _a$size === void 0 ? 'lg' : _a$size,
suffix = _a.suffix,
_a$value = _a.value,
value = _a$value === void 0 ? '' : _a$value,
props = __rest(_a, ["allowClear", "browserAutoComplete", "autoFocus", "background", "border", "className", "color", "dataSource", "debug", "highlightFirst", "inputProps", "inputType", "name", "onChange", "onFilter", "onSelect", "placeholder", "prefix", "resultTemplate", "shadow", "size", "suffix", "value"]); // Flag for autocomplete focus
var _useState = useState$2(autoFocus),
_useState2 = _slicedToArray(_useState, 2),
isFocused = _useState2[0],
setIsFocused = _useState2[1]; // The current search term
var _useState3 = useState$2(value ? "".concat(value) : ''),
_useState4 = _slicedToArray(_useState3, 2),
term = _useState4[0],
setTerm = _useState4[1];
var autoFocusRef = useRef$1(null); // Instance of the nested input
var inputRef = useRef$1();
var resultsRef = useRef$1();
var rootElement; // Handle click outside of auto complete component
var handleOutsideClick = function handleOutsideClick(e) {
if (isFocused && autoFocusRef.current && !autoFocusRef.current.contains(e.target)) {
setIsFocused(false);
}
}; // Set root element and listener for outside click
useEffect$2(function () {
rootElement = autoFocusRef.current ? autoFocusRef.current.getRootNode() : null;
if (rootElement) {
rootElement.addEventListener('click', handleOutsideClick);
}
return function () {
return rootElement.removeEventListener('click', handleOutsideClick);
};
}, [inputRef, isFocused]); // Handle updating the search term
var changeSearchTerm = function changeSearchTerm(newTerm) {
// Fire external filter event
onFilter(newTerm); // Update new term
setTerm(newTerm);
}; // Handle updating the search term
var updateInputAndTerm = function updateInputAndTerm(newTerm) {
// Update the filter
setTerm(newTerm); // Update the input
inputRef.current.update(newTerm);
}; // Handle updating the autocomplete value
var changeActiveValue = function changeActiveValue(newValue) {
changeSearchTerm(newValue.label); // Fire External Select/Change Event
onSelect(newValue.label, newValue);
onChange(newValue.label, newValue); // Update the input value
inputRef.current.update(newValue.label);
inputRef.current.blur(); // Reset the index
resultsRef.current.setActiveIndex(0);
setIsFocused(false);
};
return createElement(StyledAutoComplete, Object.assign({
ref: autoFocusRef,
shadow: shadow,
border: border,
background: background,
color: color,
className: classNames('anchor-auto-complete', className, {
focus: isFocused
})
}, props), createElement(Input, {
ariaLabel: name.length ? "auto-complete-".concat(name.toLowerCase()) : 'auto-complete',
inputProps: inputProps,
type: inputType,
value: term,
ref: inputRef,
size: size,
autoComplete: browserAutoComplete ? 'on' : 'off',
autoFocus: autoFocus,
prefix: prefix,
suffix: suffix,
placeholder: placeholder,
onFocus: function onFocus() {
return setIsFocused(true);
},
onChange: function onChange(newValue) {
changeSearchTerm(newValue);
},
onKeyDown: function onKeyDown(event) {
switch (event.keyCode) {
case EventKeyCodes.TAB:
// This should only fire if the results container exists
if (isFocused && resultsRef.current) {
// Update term of the autocomplete
resultsRef.current.updateTerm(term);
setIsFocused(false);
}
break;
case EventKeyCodes.ENTER:
if (term) {
event.preventDefault();
}
event.stopPropagation(); // This should only fire if the results container exists
if (isFocused && resultsRef.current) {
// Unset initialTerm
resultsRef.current.clearInitialTerm(); // Set the active value of the autocomplete
resultsRef.current.selectActive();
}
break;
case EventKeyCodes.ARROW_DOWN:
event.preventDefault();
event.stopPropagation();
resultsRef.current.handleNext(term);
break;
case EventKeyCodes.ARROW_UP:
event.preventDefault();
event.stopPropagation();
resultsRef.current.handlePrevious(term);
break;
default:
if (resultsRef.current) {
resultsRef.current.clearInitialTerm();
resultsRef.current.setActiveIndex(0);
}
break;
}
},
name: "auto-complete",
className: "auto-complete-input"
}), (isFocused || debug) && dataSource.length > 0 && createElement(ResultsContainer, {
size: size,
ref: resultsRef,
emitSelectedItem: function emitSelectedItem(item) {
changeActiveValue(item);
},
emitActiveTerm: function emitActiveTerm(newTerm) {
updateInputAndTerm(newTerm);
},
resultTemplate: resultTemplate,
dataSource: dataSource,
term: term,
highlightFirst: highlightFirst
}));
};
export { AutoComplete };
//# sourceMappingURL=autocomplete.js.map