@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
255 lines • 11.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importStar(require("react"));
const Theme_1 = require("../../../theme/Theme");
const util_1 = require("../../../util");
const composeEventHandlers_1 = require("../../../util/composeEventHandlers");
const hooks_1 = require("../../../util/hooks");
const filtered_options_util_1 = __importDefault(require("../FilteredOptions/filtered-options-util"));
const filteredOptionsContext_1 = require("../FilteredOptions/filteredOptionsContext");
const selectedOptionsContext_1 = require("../SelectedOptions/selectedOptionsContext");
const Input_context_1 = require("./Input.context");
const Input = (0, react_1.forwardRef)((_a, ref) => {
var { inputClassName, shouldShowSelectedOptions, placeholder, onBlur } = _a, rest = __rest(_a, ["inputClassName", "shouldShowSelectedOptions", "placeholder", "onBlur"]);
const { cn } = (0, Theme_1.useRenameCSS)();
const internalRef = (0, react_1.useRef)(null);
const mergedRefs = (0, hooks_1.useMergeRefs)(ref, internalRef);
const { clearInput, inputProps, onChange, size, value, searchTerm, setValue, hideCaret, setHideCaret, readOnly, } = (0, Input_context_1.useInputContext)();
const { selectedOptions, removeSelectedOption, toggleOption, isMultiSelect, maxSelected, } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
const { activeDecendantId, allowNewValues, currentOption, filteredOptions, isValueNew, toggleIsListOpen, isListOpen, ariaDescribedBy, setIsMouseLastUsedInputDevice, shouldAutocomplete, virtualFocus, } = (0, filteredOptionsContext_1.useFilteredOptionsContext)();
const onEnter = (0, react_1.useCallback)((event) => {
var _a;
const isSelected = (text) => selectedOptions.some((option) => option.label.toLocaleLowerCase() === text.toLocaleLowerCase());
if (currentOption) {
event.preventDefault();
// Selecting a value from the dropdown / FilteredOptions
toggleOption(currentOption, event);
if (!isMultiSelect && !isSelected(currentOption.label)) {
toggleIsListOpen(false);
}
}
else if (isSelected(value)) {
event.preventDefault();
// Trying to set the same value that is already set, so just clearing the input
clearInput(event);
}
else if ((allowNewValues || shouldAutocomplete) && value !== "") {
event.preventDefault();
const autoCompletedOption = filtered_options_util_1.default.getFirstValueStartingWith(value, filteredOptions);
/*
* User can have matching results, while not using the autocomplete result
* E.g. User types "Oslo", list has is "Oslo kommune", but user hits backspace, canceling autocomplete.
*/
const autoCompleteMatchesValue = filtered_options_util_1.default.normalizeText(value) ===
filtered_options_util_1.default.normalizeText((_a = autoCompletedOption === null || autoCompletedOption === void 0 ? void 0 : autoCompletedOption.label) !== null && _a !== void 0 ? _a : "");
let optionToToggle;
if (shouldAutocomplete &&
autoCompletedOption &&
autoCompleteMatchesValue) {
optionToToggle = autoCompletedOption;
}
else if (allowNewValues && isValueNew) {
optionToToggle = { label: value, value };
}
if (!optionToToggle) {
return;
}
toggleOption(optionToToggle, event);
if (!isMultiSelect && !isSelected(optionToToggle.label)) {
toggleIsListOpen(false);
}
}
}, [
allowNewValues,
clearInput,
currentOption,
filteredOptions,
isMultiSelect,
isValueNew,
selectedOptions,
shouldAutocomplete,
toggleIsListOpen,
toggleOption,
value,
]);
const handleKeyUp = (e) => {
e.preventDefault();
switch (e.key) {
case "Enter":
case "Accept":
onEnter(e);
break;
default:
break;
}
};
const handleKeyDown = (0, react_1.useCallback)((e) => {
setIsMouseLastUsedInputDevice(false);
if (readOnly) {
return;
}
if (e.key === "Backspace") {
if (value === "" && shouldShowSelectedOptions) {
const lastSelectedOption = selectedOptions[selectedOptions.length - 1];
if (lastSelectedOption) {
removeSelectedOption(lastSelectedOption);
}
}
}
else if (e.key === "Enter" || e.key === "Accept") {
if (activeDecendantId || value) {
e.preventDefault();
}
}
else if (e.key === "Escape") {
if (isListOpen || value) {
e.preventDefault(); // Prevents closing an encasing Modal, as Combobox reacts on keyup.
clearInput(e);
toggleIsListOpen(false);
}
}
else if (["ArrowLeft", "ArrowRight"].includes(e.key)) {
/**
* In case user has an active selection and 'completes' the selection with ArrowLeft or ArrowRight
* we need to make sure to update the filter.
*/
if (value !== "" && value !== searchTerm) {
onChange(value);
}
}
else if (e.key === "ArrowDown") {
// Reset the value to the search term to cancel autocomplete
// if the user moves focus down to the FilteredOptions
if (value !== searchTerm) {
setValue(searchTerm);
}
if (!isListOpen) {
toggleIsListOpen(true);
setTimeout(virtualFocus.moveFocusDown, 0); // Wait until list is visible so that scrollIntoView works
}
else {
virtualFocus.moveFocusDown();
}
}
else if (e.key === "ArrowUp") {
if (value !== "" && value !== searchTerm) {
onChange(value);
}
// Check that the FilteredOptions list is open and has virtual focus.
// Otherwise ignore keystrokes, so it doesn't interfere with text editing
if (isListOpen && activeDecendantId) {
e.preventDefault();
if (virtualFocus.isFocusOnTheTop()) {
toggleIsListOpen(false);
}
virtualFocus.moveFocusUp();
}
}
else if (e.key === "Home") {
e.preventDefault();
virtualFocus.moveFocusToTop();
}
else if (e.key === "End") {
e.preventDefault();
if (!isListOpen) {
toggleIsListOpen(true);
setTimeout(virtualFocus.moveFocusToBottom, 0); // Wait until list is visible so that scrollIntoView works
}
else {
virtualFocus.moveFocusToBottom();
}
}
else if (e.key === "PageUp") {
e.preventDefault();
virtualFocus.moveFocusUpBy(6);
}
else if (e.key === "PageDown") {
e.preventDefault();
if (!isListOpen) {
toggleIsListOpen(true);
setTimeout(() => virtualFocus.moveFocusDownBy(6), 0); // Wait until list is visible so that scrollIntoView works
}
else {
virtualFocus.moveFocusDownBy(6);
}
}
}, [
value,
selectedOptions,
removeSelectedOption,
isListOpen,
activeDecendantId,
setIsMouseLastUsedInputDevice,
clearInput,
toggleIsListOpen,
onChange,
virtualFocus,
setValue,
searchTerm,
shouldShowSelectedOptions,
readOnly,
]);
const onChangeHandler = (0, react_1.useCallback)((event) => {
const newValue = event.target.value;
if (newValue && newValue !== "") {
toggleIsListOpen(true);
}
else if (filteredOptions.length === 0) {
toggleIsListOpen(false);
}
onChange(newValue);
}, [filteredOptions.length, onChange, toggleIsListOpen]);
return (react_1.default.createElement("input", Object.assign({}, rest, (0, util_1.omit)(inputProps, ["aria-invalid"]), { ref: mergedRefs, type: "text", role: "combobox", value: value, onBlur: (0, composeEventHandlers_1.composeEventHandlers)(onBlur, virtualFocus.resetFocus), onClick: () => {
setHideCaret(maxSelected.isLimitReached);
value !== searchTerm && onChange(value);
}, onInput: onChangeHandler, onKeyUp: handleKeyUp, onKeyDown: handleKeyDown, autoComplete: "off", placeholder: selectedOptions.length ? undefined : placeholder, className: cn(inputClassName, "navds-combobox__input", "navds-body-short", `navds-body-short--${size}`, { "navds-combobox__input--hide-caret": hideCaret }), "aria-controls": filtered_options_util_1.default.getFilteredOptionsId(inputProps.id), "aria-expanded": !!isListOpen, "aria-autocomplete": shouldAutocomplete ? "both" : "list", "aria-activedescendant": activeDecendantId, "aria-describedby": ariaDescribedBy, "aria-invalid": inputProps["aria-invalid"], readOnly: readOnly })));
});
exports.default = Input;
//# sourceMappingURL=Input.js.map