@retailmenot/anchor
Version:
A React UI Library by RetailMeNot
633 lines (553 loc) • 21 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var __chunk_1 = require('./anchor-chunk-24f232e7.js');
var __chunk_2 = require('./anchor-chunk-9d9a5df6.js');
var form = require('./form.js');
var list = require('./list.js');
var React = require('react');
var classNames = _interopDefault(require('classnames'));
var styled = require('@xstyled/styled-components');
var styled__default = _interopDefault(styled);
var system = require('@xstyled/system');
require('./anchor-chunk-cd6fece5.js');
require('./anchor-chunk-1efd6395.js');
require('./anchor-chunk-598e53e1.js');
require('./anchor-chunk-f296150d.js');
require('./anchor-chunk-925bd1f9.js');
require('./addevent.js');
require('./anchor-chunk-31a3b978.js');
require('./arrowback.js');
require('./arrowforward.js');
require('./avataricon.js');
require('./avataroutline.js');
require('./barcode.js');
require('./bulletlist.js');
require('./calendar.js');
require('./camera.js');
require('./cart.js');
require('./cashback.js');
require('./cells.js');
require('./chat.js');
require('./check.js');
require('./checksmall.js');
require('./chevrondown.js');
require('./chevrondownsmall.js');
require('./chevronleft.js');
require('./chevronleftsmall.js');
require('./chevronright.js');
require('./chevronrightsmall.js');
require('./chevronup.js');
require('./chevronupsmall.js');
require('./clock.js');
require('./close.js');
require('./closesmall.js');
require('./commentmore.js');
require('./creditcard.js');
require('./crosshairs.js');
require('./cut.js');
require('./disabled.js');
require('./dislike.js');
require('./download.js');
require('./ellipses.js');
require('./ellipsesvertical.js');
require('./envelope.js');
require('./envelopeopen.js');
require('./error.js');
require('./expand.js');
require('./gear.js');
require('./giftcard.js');
require('./hamburger.js');
require('./heart.js');
require('./heartoutline.js');
require('./home.js');
require('./info.js');
require('./infooutline.js');
require('./laptop.js');
require('./lightning.js');
require('./like.js');
require('./listicon.js');
require('./lock.js');
require('./map.js');
require('./marker.js');
require('./markeroutline.js');
require('./mobile.js');
require('./news.js');
require('./pencil.js');
require('./play.js');
require('./plus.js');
require('./plussmall.js');
require('./print.js');
require('./question.js');
require('./questionoutline.js');
require('./refresh.js');
require('./retailmenotlogo.js');
require('./sadface.js');
require('./search.js');
require('./share.js');
require('./sliders.js');
require('./star.js');
require('./starhalf.js');
require('./staroutline.js');
require('./success.js');
require('./successoutline.js');
require('./tag.js');
require('./upload.js');
require('./tagadd.js');
function _templateObject() {
var data = __chunk_1._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 = React.useEffect,
useState = React.useState,
forwardRef = React.forwardRef,
useImperativeHandle = React.useImperativeHandle;
var ResultContainerSpaceFromAutoComplete = {
sm: '2.6rem',
md: '3.25rem',
lg: '3.25rem'
};
var StyledResultsContainer = styled__default('div')(_templateObject(), system.th.radius('base'), system.th.radius('base'), function (_ref) {
var _ref$size = _ref.size,
size = _ref$size === void 0 ? 'md' : _ref$size;
return styled.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 (__chunk_1._typeof(results[0]) === 'object') {
results.forEach(function (_a, index) {
var _a$label = _a.label,
label = _a$label === void 0 ? '' : _a$label,
props = __chunk_2.__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(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 = __chunk_2.__rest(_a, ["className", "dataSource", "emitSelectedItem", "emitActiveTerm", "highlightFirst", "term", "resultTemplate"]);
var _useState = useState(0),
_useState2 = __chunk_1._slicedToArray(_useState, 2),
currentIndex = _useState2[0],
setCurrentIndex = _useState2[1];
var _useState3 = useState(''),
_useState4 = __chunk_1._slicedToArray(_useState3, 2),
initialTerm = _useState4[0],
setInitialTerm = _useState4[1];
var results = generateResults(dataSource, currentIndex, setCurrentIndex, emitSelectedItem, term, setInitialTerm);
useEffect(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(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 React.createElement(StyledResultsContainer, Object.assign({
className: classNames('anchor-auto-complete-results-container', className)
}, props), resultTemplate ? React.createElement("div", {
className: "auto-complete-results"
}, results.map(function (_ref3, index) {
var label = _ref3.label,
value = _ref3.value;
return React.createElement(resultTemplate, {
label: label,
value: value,
term: term,
currentIndex: currentIndex,
index: relativeIndex(index),
key: relativeIndex(index)
});
})) : React.createElement(list.List, {
items: results,
className: "auto-complete-results"
}));
});
function _templateObject$1() {
var data = __chunk_1._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$1 = React.useEffect,
useState$1 = React.useState,
useRef = React.useRef;
var EventKeyCodes = {
TAB: 9,
ENTER: 13,
ARROW_UP: 38,
ARROW_DOWN: 40
};
var StyledAutoComplete = styled__default('div')(_templateObject$1(), function (_ref) {
var border = _ref.border;
return styled.css({
borderColor: border ? 'borders.base' : 'transparent'
});
}, function (_ref2) {
var backgroundColor = _ref2.background,
color = _ref2.color;
return styled.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 styled.css({
borderColor: border ? 'borders.dark' : 'transparent'
});
}, function (_ref5) {
var border = _ref5.border;
return styled.css({
borderColor: border ? 'borders.dark' : 'transparent'
});
}, system.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 = __chunk_2.__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$1(autoFocus),
_useState2 = __chunk_1._slicedToArray(_useState, 2),
isFocused = _useState2[0],
setIsFocused = _useState2[1]; // The current search term
var _useState3 = useState$1(value ? "".concat(value) : ''),
_useState4 = __chunk_1._slicedToArray(_useState3, 2),
term = _useState4[0],
setTerm = _useState4[1];
var autoFocusRef = useRef(null); // Instance of the nested input
var inputRef = useRef();
var resultsRef = useRef();
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$1(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 React.createElement(StyledAutoComplete, Object.assign({
ref: autoFocusRef,
shadow: shadow,
border: border,
background: background,
color: color,
className: classNames('anchor-auto-complete', className, {
focus: isFocused
})
}, props), React.createElement(form.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 && React.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
}));
};
exports.AutoComplete = AutoComplete;
//# sourceMappingURL=autocomplete.js.map