@navikt/ds-react 6.14.0 → 6.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/form/combobox/FilteredOptions/AddNewOption.d.ts +3 -0
- package/cjs/form/combobox/FilteredOptions/AddNewOption.js +41 -0
- package/cjs/form/combobox/FilteredOptions/AddNewOption.js.map +1 -0
- package/cjs/form/combobox/FilteredOptions/FilteredOptions.js +13 -57
- package/cjs/form/combobox/FilteredOptions/FilteredOptions.js.map +1 -1
- package/cjs/form/combobox/FilteredOptions/FilteredOptionsItem.d.ts +6 -0
- package/cjs/form/combobox/FilteredOptions/FilteredOptionsItem.js +43 -0
- package/cjs/form/combobox/FilteredOptions/FilteredOptionsItem.js.map +1 -0
- package/cjs/form/combobox/FilteredOptions/LoadingMessage.d.ts +3 -0
- package/cjs/form/combobox/FilteredOptions/LoadingMessage.js +16 -0
- package/cjs/form/combobox/FilteredOptions/LoadingMessage.js.map +1 -0
- package/cjs/form/combobox/FilteredOptions/MaxSelectedMessage.d.ts +3 -0
- package/cjs/form/combobox/FilteredOptions/MaxSelectedMessage.js +20 -0
- package/cjs/form/combobox/FilteredOptions/MaxSelectedMessage.js.map +1 -0
- package/cjs/form/combobox/FilteredOptions/NoSearchHitsMessage.d.ts +3 -0
- package/cjs/form/combobox/FilteredOptions/NoSearchHitsMessage.js +14 -0
- package/cjs/form/combobox/FilteredOptions/NoSearchHitsMessage.js.map +1 -0
- package/cjs/form/combobox/Input/Input.d.ts +1 -0
- package/cjs/form/combobox/Input/Input.js +3 -2
- package/cjs/form/combobox/Input/Input.js.map +1 -1
- package/cjs/form/combobox/Input/InputController.js +1 -1
- package/cjs/form/combobox/Input/InputController.js.map +1 -1
- package/cjs/overlays/floating-menu/Menu.d.ts +106 -0
- package/cjs/overlays/floating-menu/Menu.js +593 -0
- package/cjs/overlays/floating-menu/Menu.js.map +1 -0
- package/cjs/overlays/floating-menu/parts/FocusScope.d.ts +22 -0
- package/cjs/overlays/floating-menu/parts/FocusScope.js +89 -0
- package/cjs/overlays/floating-menu/parts/FocusScope.js.map +1 -0
- package/cjs/overlays/floating-menu/parts/RovingFocus.d.ts +9 -0
- package/cjs/overlays/floating-menu/parts/RovingFocus.js +112 -0
- package/cjs/overlays/floating-menu/parts/RovingFocus.js.map +1 -0
- package/cjs/overlays/floating-menu/parts/SlottedDivElement.d.ts +7 -0
- package/cjs/overlays/floating-menu/parts/SlottedDivElement.js +46 -0
- package/cjs/overlays/floating-menu/parts/SlottedDivElement.js.map +1 -0
- package/cjs/util/composeEventHandlers.d.ts +1 -1
- package/esm/form/combobox/FilteredOptions/AddNewOption.d.ts +3 -0
- package/esm/form/combobox/FilteredOptions/AddNewOption.js +36 -0
- package/esm/form/combobox/FilteredOptions/AddNewOption.js.map +1 -0
- package/esm/form/combobox/FilteredOptions/FilteredOptions.js +13 -57
- package/esm/form/combobox/FilteredOptions/FilteredOptions.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/FilteredOptionsItem.d.ts +6 -0
- package/esm/form/combobox/FilteredOptions/FilteredOptionsItem.js +38 -0
- package/esm/form/combobox/FilteredOptions/FilteredOptionsItem.js.map +1 -0
- package/esm/form/combobox/FilteredOptions/LoadingMessage.d.ts +3 -0
- package/esm/form/combobox/FilteredOptions/LoadingMessage.js +11 -0
- package/esm/form/combobox/FilteredOptions/LoadingMessage.js.map +1 -0
- package/esm/form/combobox/FilteredOptions/MaxSelectedMessage.d.ts +3 -0
- package/esm/form/combobox/FilteredOptions/MaxSelectedMessage.js +15 -0
- package/esm/form/combobox/FilteredOptions/MaxSelectedMessage.js.map +1 -0
- package/esm/form/combobox/FilteredOptions/NoSearchHitsMessage.d.ts +3 -0
- package/esm/form/combobox/FilteredOptions/NoSearchHitsMessage.js +9 -0
- package/esm/form/combobox/FilteredOptions/NoSearchHitsMessage.js.map +1 -0
- package/esm/form/combobox/Input/Input.d.ts +1 -0
- package/esm/form/combobox/Input/Input.js +3 -2
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/form/combobox/Input/InputController.js +1 -1
- package/esm/form/combobox/Input/InputController.js.map +1 -1
- package/esm/overlays/floating-menu/Menu.d.ts +106 -0
- package/esm/overlays/floating-menu/Menu.js +551 -0
- package/esm/overlays/floating-menu/Menu.js.map +1 -0
- package/esm/overlays/floating-menu/parts/FocusScope.d.ts +22 -0
- package/esm/overlays/floating-menu/parts/FocusScope.js +63 -0
- package/esm/overlays/floating-menu/parts/FocusScope.js.map +1 -0
- package/esm/overlays/floating-menu/parts/RovingFocus.d.ts +9 -0
- package/esm/overlays/floating-menu/parts/RovingFocus.js +86 -0
- package/esm/overlays/floating-menu/parts/RovingFocus.js.map +1 -0
- package/esm/overlays/floating-menu/parts/SlottedDivElement.d.ts +7 -0
- package/esm/overlays/floating-menu/parts/SlottedDivElement.js +20 -0
- package/esm/overlays/floating-menu/parts/SlottedDivElement.js.map +1 -0
- package/esm/util/composeEventHandlers.d.ts +1 -1
- package/package.json +3 -3
- package/src/form/combobox/FilteredOptions/AddNewOption.tsx +63 -0
- package/src/form/combobox/FilteredOptions/FilteredOptions.tsx +11 -121
- package/src/form/combobox/FilteredOptions/FilteredOptionsItem.tsx +73 -0
- package/src/form/combobox/FilteredOptions/LoadingMessage.tsx +20 -0
- package/src/form/combobox/FilteredOptions/MaxSelectedMessage.tsx +27 -0
- package/src/form/combobox/FilteredOptions/NoSearchHitsMessage.tsx +19 -0
- package/src/form/combobox/Input/Input.tsx +4 -2
- package/src/form/combobox/Input/InputController.tsx +1 -0
- package/src/overlays/floating-menu/Menu.tsx +1177 -0
- package/src/overlays/floating-menu/parts/FocusScope.tsx +84 -0
- package/src/overlays/floating-menu/parts/RovingFocus.tsx +121 -0
- package/src/overlays/floating-menu/parts/SlottedDivElement.tsx +17 -0
- package/src/util/composeEventHandlers.ts +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
import { forwardRef, useEffect, useState } from "react";
|
|
14
|
+
import { Slot } from "../../../slot/Slot.js";
|
|
15
|
+
import { useCallbackRef, useMergeRefs } from "../../../util/hooks/index.js";
|
|
16
|
+
const AUTOFOCUS_ON_MOUNT = "focusScope.autoFocusOnMount";
|
|
17
|
+
const AUTOFOCUS_ON_UNMOUNT = "focusScope.autoFocusOnUnmount";
|
|
18
|
+
const EVENT_OPTIONS = { bubbles: false, cancelable: true };
|
|
19
|
+
/**
|
|
20
|
+
* FocusScope manages focus on mount and unmount of container.
|
|
21
|
+
* This is used to better handle autofocus of elements when mounted and unmounted.
|
|
22
|
+
* Example usage:
|
|
23
|
+
* - Focus first item in a list when mounted
|
|
24
|
+
* - Focus a button when unmounted
|
|
25
|
+
*/
|
|
26
|
+
const FocusScope = forwardRef((_a, ref) => {
|
|
27
|
+
var { onMountHandler: onMountHandlerCallback, onUnmountHandler: onUnmountHandlerCallback } = _a, rest = __rest(_a, ["onMountHandler", "onUnmountHandler"]);
|
|
28
|
+
const [container, setContainer] = useState(null);
|
|
29
|
+
const onMountHandler = useCallbackRef(onMountHandlerCallback);
|
|
30
|
+
const onUnmountHandler = useCallbackRef(onUnmountHandlerCallback);
|
|
31
|
+
const composedRefs = useMergeRefs(ref, setContainer);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
var _a;
|
|
34
|
+
if (!container)
|
|
35
|
+
return;
|
|
36
|
+
const ownerDocument = (_a = container.ownerDocument) !== null && _a !== void 0 ? _a : globalThis === null || globalThis === void 0 ? void 0 : globalThis.document;
|
|
37
|
+
const hasFocus = container.contains(ownerDocument.activeElement);
|
|
38
|
+
if (!hasFocus) {
|
|
39
|
+
const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS);
|
|
40
|
+
container.addEventListener(AUTOFOCUS_ON_MOUNT, onMountHandler);
|
|
41
|
+
container.dispatchEvent(mountEvent);
|
|
42
|
+
}
|
|
43
|
+
return () => {
|
|
44
|
+
container.removeEventListener(AUTOFOCUS_ON_MOUNT, onMountHandler);
|
|
45
|
+
/**
|
|
46
|
+
* https://github.com/facebook/react/issues/17894
|
|
47
|
+
* As usual when dealing with focus and useEffect,
|
|
48
|
+
* we need to defer the focus to the next event-loop
|
|
49
|
+
* setTimeout makes sure the code is ran after the next render-cycle
|
|
50
|
+
*/
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
const unmountEvent = new CustomEvent(AUTOFOCUS_ON_UNMOUNT, EVENT_OPTIONS);
|
|
53
|
+
container.addEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountHandler);
|
|
54
|
+
container.dispatchEvent(unmountEvent);
|
|
55
|
+
// we need to remove the listener after we `dispatchEvent`
|
|
56
|
+
container.removeEventListener(AUTOFOCUS_ON_UNMOUNT, onUnmountHandler);
|
|
57
|
+
}, 0);
|
|
58
|
+
};
|
|
59
|
+
}, [container, onMountHandler, onUnmountHandler]);
|
|
60
|
+
return React.createElement(Slot, Object.assign({ tabIndex: -1 }, rest, { ref: composedRefs }));
|
|
61
|
+
});
|
|
62
|
+
export { FocusScope };
|
|
63
|
+
//# sourceMappingURL=FocusScope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FocusScope.js","sourceRoot":"","sources":["../../../../src/overlays/floating-menu/parts/FocusScope.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnE,MAAM,kBAAkB,GAAG,6BAA6B,CAAC;AACzD,MAAM,oBAAoB,GAAG,+BAA+B,CAAC;AAC7D,MAAM,aAAa,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAe3D;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,UAAU,CAC3B,CACE,EAIC,EACD,GAAG,EACH,EAAE;QANF,EACE,cAAc,EAAE,sBAAsB,EACtC,gBAAgB,EAAE,wBAAwB,OAE3C,EADI,IAAI,cAHT,sCAIC,CADQ;IAIT,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC,CAAC;IACrE,MAAM,cAAc,GAAG,cAAc,CAAC,sBAAsB,CAAC,CAAC;IAC9D,MAAM,gBAAgB,GAAG,cAAc,CAAC,wBAAwB,CAAC,CAAC;IAElE,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAErD,SAAS,CAAC,GAAG,EAAE;;QACb,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,aAAa,GAAG,MAAA,SAAS,CAAC,aAAa,mCAAI,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,QAAQ,CAAC;QACtE,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAEjE,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,UAAU,GAAG,IAAI,WAAW,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;YACtE,SAAS,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;YAC/D,SAAS,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,mBAAmB,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;YAElE;;;;;eAKG;YACH,UAAU,CAAC,GAAG,EAAE;gBACd,MAAM,YAAY,GAAG,IAAI,WAAW,CAClC,oBAAoB,EACpB,aAAa,CACd,CAAC;gBACF,SAAS,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;gBACnE,SAAS,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;gBAEtC,0DAA0D;gBAC1D,SAAS,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,gBAAgB,CAAC,CAAC;YACxE,CAAC,EAAE,CAAC,CAAC,CAAC;QACR,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAElD,OAAO,oBAAC,IAAI,kBAAC,QAAQ,EAAE,CAAC,CAAC,IAAM,IAAI,IAAE,GAAG,EAAE,YAAY,IAAI,CAAC;AAC7D,CAAC,CACF,CAAC;AAEF,OAAO,EAAE,UAAU,EAAwB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DescendantsManager } from "../../../util/hooks/descendants/descendant.js";
|
|
3
|
+
interface RovingFocusProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "tabIndex"> {
|
|
4
|
+
asChild?: boolean;
|
|
5
|
+
descendants: DescendantsManager<HTMLDivElement, object>;
|
|
6
|
+
onEntryFocus?: (event: Event) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const RovingFocus: React.ForwardRefExoticComponent<RovingFocusProps & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
export { RovingFocus, type RovingFocusProps };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { forwardRef, useCallback, useEffect, useRef } from "react";
|
|
13
|
+
import { Slot } from "../../../slot/Slot.js";
|
|
14
|
+
import { composeEventHandlers } from "../../../util/composeEventHandlers.js";
|
|
15
|
+
import { useCallbackRef, useMergeRefs } from "../../../util/hooks/index.js";
|
|
16
|
+
const ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
|
|
17
|
+
const EVENT_OPTIONS = { bubbles: false, cancelable: true };
|
|
18
|
+
const RovingFocus = forwardRef((_a, ref) => {
|
|
19
|
+
var { children, asChild, descendants, onKeyDown, onEntryFocus, onMouseDown, onFocus } = _a, rest = __rest(_a, ["children", "asChild", "descendants", "onKeyDown", "onEntryFocus", "onMouseDown", "onFocus"]);
|
|
20
|
+
const _ref = React.useRef(null);
|
|
21
|
+
const composedRefs = useMergeRefs(ref, _ref);
|
|
22
|
+
const handleEntryFocus = useCallbackRef(onEntryFocus);
|
|
23
|
+
const isMouseFocusRef = useRef(false);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const node = _ref.current;
|
|
26
|
+
if (node) {
|
|
27
|
+
node.addEventListener(ENTRY_FOCUS, handleEntryFocus);
|
|
28
|
+
return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);
|
|
29
|
+
}
|
|
30
|
+
}, [handleEntryFocus]);
|
|
31
|
+
const handleKeyDown = useCallback((event) => {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
const loop = false;
|
|
34
|
+
const ownerDocument = (_b = (_a = _ref === null || _ref === void 0 ? void 0 : _ref.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) !== null && _b !== void 0 ? _b : globalThis === null || globalThis === void 0 ? void 0 : globalThis.document;
|
|
35
|
+
const idx = descendants
|
|
36
|
+
.values()
|
|
37
|
+
.findIndex((x) => x.node.isSameNode(ownerDocument.activeElement));
|
|
38
|
+
const nextItem = () => {
|
|
39
|
+
var _a;
|
|
40
|
+
const next = descendants.nextEnabled(idx, loop);
|
|
41
|
+
next && ((_a = next.node) === null || _a === void 0 ? void 0 : _a.focus());
|
|
42
|
+
};
|
|
43
|
+
const prevItem = () => {
|
|
44
|
+
var _a;
|
|
45
|
+
const prev = descendants.prevEnabled(idx, loop);
|
|
46
|
+
prev && ((_a = prev.node) === null || _a === void 0 ? void 0 : _a.focus());
|
|
47
|
+
};
|
|
48
|
+
const firstItem = () => {
|
|
49
|
+
var _a;
|
|
50
|
+
const first = descendants.firstEnabled();
|
|
51
|
+
first && ((_a = first.node) === null || _a === void 0 ? void 0 : _a.focus());
|
|
52
|
+
};
|
|
53
|
+
const lastItem = () => {
|
|
54
|
+
var _a;
|
|
55
|
+
const last = descendants.lastEnabled();
|
|
56
|
+
last && ((_a = last.node) === null || _a === void 0 ? void 0 : _a.focus());
|
|
57
|
+
};
|
|
58
|
+
const keyMap = {
|
|
59
|
+
ArrowUp: prevItem,
|
|
60
|
+
ArrowDown: nextItem,
|
|
61
|
+
Home: firstItem,
|
|
62
|
+
End: lastItem,
|
|
63
|
+
};
|
|
64
|
+
const action = keyMap[event.key];
|
|
65
|
+
if (action) {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
action(event);
|
|
68
|
+
}
|
|
69
|
+
}, [descendants]);
|
|
70
|
+
const Comp = asChild ? Slot : "div";
|
|
71
|
+
return (React.createElement(Comp, Object.assign({ ref: composedRefs }, rest, { tabIndex: descendants.enabledCount() === 0 ? -1 : 0, style: Object.assign({ outline: "none" }, rest.style), onKeyDown: composeEventHandlers(onKeyDown, handleKeyDown), onMouseDown: composeEventHandlers(onMouseDown, () => {
|
|
72
|
+
isMouseFocusRef.current = true;
|
|
73
|
+
}), onFocus: composeEventHandlers(onFocus, (event) => {
|
|
74
|
+
var _a;
|
|
75
|
+
if (event.target === event.currentTarget) {
|
|
76
|
+
const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
|
|
77
|
+
event.currentTarget.dispatchEvent(entryFocusEvent);
|
|
78
|
+
if (!entryFocusEvent.defaultPrevented) {
|
|
79
|
+
(_a = descendants.firstEnabled()) === null || _a === void 0 ? void 0 : _a.node.focus({ preventScroll: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
isMouseFocusRef.current = false;
|
|
83
|
+
}) }), children));
|
|
84
|
+
});
|
|
85
|
+
export { RovingFocus };
|
|
86
|
+
//# sourceMappingURL=RovingFocus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RovingFocus.js","sourceRoot":"","sources":["../../../../src/overlays/floating-menu/parts/RovingFocus.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface SlottedDivProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
asChild?: boolean;
|
|
4
|
+
}
|
|
5
|
+
declare const SlottedDivElement: React.ForwardRefExoticComponent<SlottedDivProps & React.RefAttributes<HTMLDivElement>>;
|
|
6
|
+
type SlottedDivElementRef = React.ElementRef<typeof SlottedDivElement>;
|
|
7
|
+
export { SlottedDivElement, type SlottedDivElementRef, type SlottedDivProps };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import React, { forwardRef } from "react";
|
|
13
|
+
import { Slot } from "../../../slot/Slot.js";
|
|
14
|
+
const SlottedDivElement = forwardRef((_a, forwardedRef) => {
|
|
15
|
+
var { asChild } = _a, rest = __rest(_a, ["asChild"]);
|
|
16
|
+
const Comp = asChild ? Slot : "div";
|
|
17
|
+
return React.createElement(Comp, Object.assign({}, rest, { ref: forwardedRef }));
|
|
18
|
+
});
|
|
19
|
+
export { SlottedDivElement };
|
|
20
|
+
//# sourceMappingURL=SlottedDivElement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlottedDivElement.js","sourceRoot":"","sources":["../../../../src/overlays/floating-menu/parts/SlottedDivElement.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAM1C,MAAM,iBAAiB,GAAG,UAAU,CAClC,CAAC,EAAoB,EAAE,YAAY,EAAE,EAAE;QAAtC,EAAE,OAAO,OAAW,EAAN,IAAI,cAAlB,WAAoB,CAAF;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACpC,OAAO,oBAAC,IAAI,oBAAK,IAAI,IAAE,GAAG,EAAE,YAAY,IAAI,CAAC;AAC/C,CAAC,CACF,CAAC;AAIF,OAAO,EAAE,iBAAiB,EAAmD,CAAC"}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Utility to consistently call original eventhandler, often from props and internal eventhandler
|
|
3
3
|
* @internal
|
|
4
4
|
*/
|
|
5
|
-
export declare function composeEventHandlers<T extends React.SyntheticEvent>(originalEventHandler?: (event: T) => void, ourEventHandler?: (event: T) => void, { checkForDefaultPrevented }?: {
|
|
5
|
+
export declare function composeEventHandlers<T extends React.SyntheticEvent | Event>(originalEventHandler?: (event: T) => void, ourEventHandler?: (event: T) => void, { checkForDefaultPrevented }?: {
|
|
6
6
|
checkForDefaultPrevented?: boolean | undefined;
|
|
7
7
|
}): (event: T) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@navikt/ds-react",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.15.0",
|
|
4
4
|
"description": "React components from the Norwegian Labour and Welfare Administration.",
|
|
5
5
|
"author": "Aksel, a team part of the Norwegian Labour and Welfare Administration.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -594,8 +594,8 @@
|
|
|
594
594
|
"dependencies": {
|
|
595
595
|
"@floating-ui/react": "0.25.4",
|
|
596
596
|
"@floating-ui/react-dom": "^2.0.9",
|
|
597
|
-
"@navikt/aksel-icons": "^6.
|
|
598
|
-
"@navikt/ds-tokens": "^6.
|
|
597
|
+
"@navikt/aksel-icons": "^6.15.0",
|
|
598
|
+
"@navikt/ds-tokens": "^6.15.0",
|
|
599
599
|
"clsx": "^2.1.0",
|
|
600
600
|
"date-fns": "^3.0.0",
|
|
601
601
|
"react-day-picker": "8.10.0"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import cl from "clsx";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { PlusIcon } from "@navikt/aksel-icons";
|
|
4
|
+
import { BodyShort, Label } from "../../../typography";
|
|
5
|
+
import { useInputContext } from "../Input/Input.context";
|
|
6
|
+
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
|
7
|
+
import { isInList, toComboboxOption } from "../combobox-utils";
|
|
8
|
+
import filteredOptionsUtil from "./filtered-options-util";
|
|
9
|
+
import { useFilteredOptionsContext } from "./filteredOptionsContext";
|
|
10
|
+
|
|
11
|
+
const AddNewOption = () => {
|
|
12
|
+
const {
|
|
13
|
+
inputProps: { id },
|
|
14
|
+
size,
|
|
15
|
+
value,
|
|
16
|
+
} = useInputContext();
|
|
17
|
+
const {
|
|
18
|
+
setIsMouseLastUsedInputDevice,
|
|
19
|
+
toggleIsListOpen,
|
|
20
|
+
activeDecendantId,
|
|
21
|
+
virtualFocus,
|
|
22
|
+
} = useFilteredOptionsContext();
|
|
23
|
+
const { isMultiSelect, selectedOptions, toggleOption } =
|
|
24
|
+
useSelectedOptionsContext();
|
|
25
|
+
return (
|
|
26
|
+
<li
|
|
27
|
+
tabIndex={-1}
|
|
28
|
+
onMouseMove={() => {
|
|
29
|
+
if (activeDecendantId !== filteredOptionsUtil.getAddNewOptionId(id)) {
|
|
30
|
+
virtualFocus.moveFocusToElement(
|
|
31
|
+
filteredOptionsUtil.getAddNewOptionId(id),
|
|
32
|
+
);
|
|
33
|
+
setIsMouseLastUsedInputDevice(true);
|
|
34
|
+
}
|
|
35
|
+
}}
|
|
36
|
+
onPointerUp={(event) => {
|
|
37
|
+
toggleOption(toComboboxOption(value), event);
|
|
38
|
+
if (!isMultiSelect && !isInList(value, selectedOptions))
|
|
39
|
+
toggleIsListOpen(false);
|
|
40
|
+
}}
|
|
41
|
+
id={filteredOptionsUtil.getAddNewOptionId(id)}
|
|
42
|
+
className={cl(
|
|
43
|
+
"navds-combobox__list-item navds-combobox__list-item--new-option",
|
|
44
|
+
{
|
|
45
|
+
"navds-combobox__list-item--new-option--focus":
|
|
46
|
+
activeDecendantId === filteredOptionsUtil.getAddNewOptionId(id),
|
|
47
|
+
},
|
|
48
|
+
)}
|
|
49
|
+
role="option"
|
|
50
|
+
aria-selected={false}
|
|
51
|
+
>
|
|
52
|
+
<PlusIcon aria-hidden />
|
|
53
|
+
<BodyShort size={size}>
|
|
54
|
+
Legg til{" "}
|
|
55
|
+
<Label as="span" size={size}>
|
|
56
|
+
“{value}”
|
|
57
|
+
</Label>
|
|
58
|
+
</BodyShort>
|
|
59
|
+
</li>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export default AddNewOption;
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import cl from "clsx";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { CheckmarkIcon, PlusIcon } from "@navikt/aksel-icons";
|
|
4
|
-
import { Loader } from "../../../loader";
|
|
5
|
-
import { BodyShort, Label } from "../../../typography";
|
|
6
3
|
import { useInputContext } from "../Input/Input.context";
|
|
7
4
|
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
|
8
|
-
import
|
|
9
|
-
import
|
|
5
|
+
import AddNewOption from "./AddNewOption";
|
|
6
|
+
import FilteredOptionsItem from "./FilteredOptionsItem";
|
|
7
|
+
import LoadingMessage from "./LoadingMessage";
|
|
8
|
+
import MaxSelectedMessage from "./MaxSelectedMessage";
|
|
9
|
+
import NoSearchHitsMessage from "./NoSearchHitsMessage";
|
|
10
10
|
import filteredOptionsUtil from "./filtered-options-util";
|
|
11
11
|
import { useFilteredOptionsContext } from "./filteredOptionsContext";
|
|
12
12
|
|
|
13
13
|
const FilteredOptions = () => {
|
|
14
14
|
const {
|
|
15
15
|
inputProps: { id },
|
|
16
|
-
size,
|
|
17
|
-
value,
|
|
18
16
|
} = useInputContext();
|
|
19
17
|
const {
|
|
20
18
|
allowNewValues,
|
|
@@ -23,17 +21,9 @@ const FilteredOptions = () => {
|
|
|
23
21
|
filteredOptions,
|
|
24
22
|
setFilteredOptionsRef,
|
|
25
23
|
isMouseLastUsedInputDevice,
|
|
26
|
-
setIsMouseLastUsedInputDevice,
|
|
27
24
|
isValueNew,
|
|
28
|
-
toggleIsListOpen,
|
|
29
|
-
activeDecendantId,
|
|
30
|
-
virtualFocus,
|
|
31
25
|
} = useFilteredOptionsContext();
|
|
32
|
-
const {
|
|
33
|
-
useSelectedOptionsContext();
|
|
34
|
-
|
|
35
|
-
const isDisabled = (option: ComboboxOption) =>
|
|
36
|
-
maxSelected?.isLimitReached && !isInList(option.value, selectedOptions);
|
|
26
|
+
const { maxSelected } = useSelectedOptionsContext();
|
|
37
27
|
|
|
38
28
|
const shouldRenderNonSelectables =
|
|
39
29
|
maxSelected?.isLimitReached || // Render maxSelected message
|
|
@@ -55,30 +45,10 @@ const FilteredOptions = () => {
|
|
|
55
45
|
>
|
|
56
46
|
{shouldRenderNonSelectables && (
|
|
57
47
|
<div className="navds-combobox__list_non-selectables" role="status">
|
|
58
|
-
{maxSelected?.isLimitReached &&
|
|
59
|
-
|
|
60
|
-
className="navds-combobox__list-item--max-selected"
|
|
61
|
-
id={filteredOptionsUtil.getMaxSelectedOptionsId(id)}
|
|
62
|
-
>
|
|
63
|
-
{maxSelected.message ??
|
|
64
|
-
`${selectedOptions.length} av ${maxSelected.limit} er valgt.`}
|
|
65
|
-
</div>
|
|
66
|
-
)}
|
|
67
|
-
{isLoading && (
|
|
68
|
-
<div
|
|
69
|
-
className="navds-combobox__list-item--loading"
|
|
70
|
-
id={filteredOptionsUtil.getIsLoadingId(id)}
|
|
71
|
-
>
|
|
72
|
-
<Loader title="Søker..." />
|
|
73
|
-
</div>
|
|
74
|
-
)}
|
|
48
|
+
{maxSelected?.isLimitReached && <MaxSelectedMessage />}
|
|
49
|
+
{isLoading && <LoadingMessage />}
|
|
75
50
|
{!isLoading && filteredOptions.length === 0 && !allowNewValues && (
|
|
76
|
-
<
|
|
77
|
-
className="navds-combobox__list-item--no-options"
|
|
78
|
-
id={filteredOptionsUtil.getNoHitsId(id)}
|
|
79
|
-
>
|
|
80
|
-
Ingen søketreff
|
|
81
|
-
</div>
|
|
51
|
+
<NoSearchHitsMessage />
|
|
82
52
|
)}
|
|
83
53
|
</div>
|
|
84
54
|
)}
|
|
@@ -90,90 +60,10 @@ const FilteredOptions = () => {
|
|
|
90
60
|
className="navds-combobox__list-options"
|
|
91
61
|
>
|
|
92
62
|
{isValueNew && !maxSelected?.isLimitReached && allowNewValues && (
|
|
93
|
-
<
|
|
94
|
-
tabIndex={-1}
|
|
95
|
-
onMouseMove={() => {
|
|
96
|
-
if (
|
|
97
|
-
activeDecendantId !==
|
|
98
|
-
filteredOptionsUtil.getAddNewOptionId(id)
|
|
99
|
-
) {
|
|
100
|
-
virtualFocus.moveFocusToElement(
|
|
101
|
-
filteredOptionsUtil.getAddNewOptionId(id),
|
|
102
|
-
);
|
|
103
|
-
setIsMouseLastUsedInputDevice(true);
|
|
104
|
-
}
|
|
105
|
-
}}
|
|
106
|
-
onPointerUp={(event) => {
|
|
107
|
-
toggleOption(toComboboxOption(value), event);
|
|
108
|
-
if (!isMultiSelect && !isInList(value, selectedOptions))
|
|
109
|
-
toggleIsListOpen(false);
|
|
110
|
-
}}
|
|
111
|
-
id={filteredOptionsUtil.getAddNewOptionId(id)}
|
|
112
|
-
className={cl(
|
|
113
|
-
"navds-combobox__list-item navds-combobox__list-item--new-option",
|
|
114
|
-
{
|
|
115
|
-
"navds-combobox__list-item--new-option--focus":
|
|
116
|
-
activeDecendantId ===
|
|
117
|
-
filteredOptionsUtil.getAddNewOptionId(id),
|
|
118
|
-
},
|
|
119
|
-
)}
|
|
120
|
-
role="option"
|
|
121
|
-
aria-selected={false}
|
|
122
|
-
>
|
|
123
|
-
<PlusIcon aria-hidden />
|
|
124
|
-
<BodyShort size={size}>
|
|
125
|
-
Legg til{" "}
|
|
126
|
-
<Label as="span" size={size}>
|
|
127
|
-
“{value}”
|
|
128
|
-
</Label>
|
|
129
|
-
</BodyShort>
|
|
130
|
-
</li>
|
|
63
|
+
<AddNewOption />
|
|
131
64
|
)}
|
|
132
65
|
{filteredOptions.map((option) => (
|
|
133
|
-
<
|
|
134
|
-
className={cl("navds-combobox__list-item", {
|
|
135
|
-
"navds-combobox__list-item--focus":
|
|
136
|
-
activeDecendantId ===
|
|
137
|
-
filteredOptionsUtil.getOptionId(id, option.label),
|
|
138
|
-
"navds-combobox__list-item--selected": isInList(
|
|
139
|
-
option.value,
|
|
140
|
-
selectedOptions,
|
|
141
|
-
),
|
|
142
|
-
})}
|
|
143
|
-
data-no-focus={isDisabled(option) || undefined}
|
|
144
|
-
id={filteredOptionsUtil.getOptionId(id, option.label)}
|
|
145
|
-
key={option.label}
|
|
146
|
-
tabIndex={-1}
|
|
147
|
-
onMouseMove={() => {
|
|
148
|
-
if (
|
|
149
|
-
activeDecendantId !==
|
|
150
|
-
filteredOptionsUtil.getOptionId(id, option.label)
|
|
151
|
-
) {
|
|
152
|
-
virtualFocus.moveFocusToElement(
|
|
153
|
-
filteredOptionsUtil.getOptionId(id, option.label),
|
|
154
|
-
);
|
|
155
|
-
setIsMouseLastUsedInputDevice(true);
|
|
156
|
-
}
|
|
157
|
-
}}
|
|
158
|
-
onPointerUp={(event) => {
|
|
159
|
-
if (isDisabled(option)) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
toggleOption(option, event);
|
|
163
|
-
if (
|
|
164
|
-
!isMultiSelect &&
|
|
165
|
-
!isInList(option.value, selectedOptions)
|
|
166
|
-
) {
|
|
167
|
-
toggleIsListOpen(false);
|
|
168
|
-
}
|
|
169
|
-
}}
|
|
170
|
-
role="option"
|
|
171
|
-
aria-selected={isInList(option.value, selectedOptions)}
|
|
172
|
-
aria-disabled={isDisabled(option) || undefined}
|
|
173
|
-
>
|
|
174
|
-
<BodyShort size={size}>{option.label}</BodyShort>
|
|
175
|
-
{isInList(option.value, selectedOptions) && <CheckmarkIcon />}
|
|
176
|
-
</li>
|
|
66
|
+
<FilteredOptionsItem key={option.value} option={option} />
|
|
177
67
|
))}
|
|
178
68
|
</ul>
|
|
179
69
|
)}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import cl from "clsx";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { CheckmarkIcon } from "@navikt/aksel-icons";
|
|
4
|
+
import { BodyShort } from "../../../typography";
|
|
5
|
+
import { useInputContext } from "../Input/Input.context";
|
|
6
|
+
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
|
7
|
+
import { isInList } from "../combobox-utils";
|
|
8
|
+
import { ComboboxOption } from "../types";
|
|
9
|
+
import filteredOptionsUtil from "./filtered-options-util";
|
|
10
|
+
import { useFilteredOptionsContext } from "./filteredOptionsContext";
|
|
11
|
+
|
|
12
|
+
const FilteredOptionsItem = ({ option }: { option: ComboboxOption }) => {
|
|
13
|
+
const {
|
|
14
|
+
inputProps: { id },
|
|
15
|
+
size,
|
|
16
|
+
} = useInputContext();
|
|
17
|
+
const {
|
|
18
|
+
setIsMouseLastUsedInputDevice,
|
|
19
|
+
toggleIsListOpen,
|
|
20
|
+
activeDecendantId,
|
|
21
|
+
virtualFocus,
|
|
22
|
+
} = useFilteredOptionsContext();
|
|
23
|
+
const { isMultiSelect, maxSelected, selectedOptions, toggleOption } =
|
|
24
|
+
useSelectedOptionsContext();
|
|
25
|
+
|
|
26
|
+
const isDisabled = (_option: ComboboxOption) =>
|
|
27
|
+
maxSelected?.isLimitReached && !isInList(_option.value, selectedOptions);
|
|
28
|
+
return (
|
|
29
|
+
<li
|
|
30
|
+
className={cl("navds-combobox__list-item", {
|
|
31
|
+
"navds-combobox__list-item--focus":
|
|
32
|
+
activeDecendantId ===
|
|
33
|
+
filteredOptionsUtil.getOptionId(id, option.label),
|
|
34
|
+
"navds-combobox__list-item--selected": isInList(
|
|
35
|
+
option.value,
|
|
36
|
+
selectedOptions,
|
|
37
|
+
),
|
|
38
|
+
})}
|
|
39
|
+
data-no-focus={isDisabled(option) || undefined}
|
|
40
|
+
id={filteredOptionsUtil.getOptionId(id, option.label)}
|
|
41
|
+
key={option.label}
|
|
42
|
+
tabIndex={-1}
|
|
43
|
+
onMouseMove={() => {
|
|
44
|
+
if (
|
|
45
|
+
activeDecendantId !==
|
|
46
|
+
filteredOptionsUtil.getOptionId(id, option.label)
|
|
47
|
+
) {
|
|
48
|
+
virtualFocus.moveFocusToElement(
|
|
49
|
+
filteredOptionsUtil.getOptionId(id, option.label),
|
|
50
|
+
);
|
|
51
|
+
setIsMouseLastUsedInputDevice(true);
|
|
52
|
+
}
|
|
53
|
+
}}
|
|
54
|
+
onPointerUp={(event) => {
|
|
55
|
+
if (isDisabled(option)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
toggleOption(option, event);
|
|
59
|
+
if (!isMultiSelect && !isInList(option.value, selectedOptions)) {
|
|
60
|
+
toggleIsListOpen(false);
|
|
61
|
+
}
|
|
62
|
+
}}
|
|
63
|
+
role="option"
|
|
64
|
+
aria-selected={isInList(option.value, selectedOptions)}
|
|
65
|
+
aria-disabled={isDisabled(option) || undefined}
|
|
66
|
+
>
|
|
67
|
+
<BodyShort size={size}>{option.label}</BodyShort>
|
|
68
|
+
{isInList(option.value, selectedOptions) && <CheckmarkIcon />}
|
|
69
|
+
</li>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default FilteredOptionsItem;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Loader } from "../../../loader";
|
|
3
|
+
import { useInputContext } from "../Input/Input.context";
|
|
4
|
+
import filteredOptionsUtil from "./filtered-options-util";
|
|
5
|
+
|
|
6
|
+
const LoadingMessage = () => {
|
|
7
|
+
const {
|
|
8
|
+
inputProps: { id },
|
|
9
|
+
} = useInputContext();
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
className="navds-combobox__list-item--loading"
|
|
13
|
+
id={filteredOptionsUtil.getIsLoadingId(id)}
|
|
14
|
+
>
|
|
15
|
+
<Loader title="Søker..." />
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default LoadingMessage;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useInputContext } from "../Input/Input.context";
|
|
3
|
+
import { useSelectedOptionsContext } from "../SelectedOptions/selectedOptionsContext";
|
|
4
|
+
import filteredOptionsUtil from "./filtered-options-util";
|
|
5
|
+
|
|
6
|
+
const MaxSelectedMessage = () => {
|
|
7
|
+
const {
|
|
8
|
+
inputProps: { id },
|
|
9
|
+
} = useInputContext();
|
|
10
|
+
const { maxSelected, selectedOptions } = useSelectedOptionsContext();
|
|
11
|
+
|
|
12
|
+
if (!maxSelected) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
className="navds-combobox__list-item--max-selected"
|
|
19
|
+
id={filteredOptionsUtil.getMaxSelectedOptionsId(id)}
|
|
20
|
+
>
|
|
21
|
+
{maxSelected.message ??
|
|
22
|
+
`${selectedOptions.length} av ${maxSelected.limit} er valgt.`}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default MaxSelectedMessage;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useInputContext } from "../Input/Input.context";
|
|
3
|
+
import filteredOptionsUtil from "./filtered-options-util";
|
|
4
|
+
|
|
5
|
+
const NoSearchHitsMessage = () => {
|
|
6
|
+
const {
|
|
7
|
+
inputProps: { id },
|
|
8
|
+
} = useInputContext();
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
className="navds-combobox__list-item--no-options"
|
|
12
|
+
id={filteredOptionsUtil.getNoHitsId(id)}
|
|
13
|
+
>
|
|
14
|
+
Ingen søketreff
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default NoSearchHitsMessage;
|
|
@@ -16,11 +16,12 @@ interface InputProps
|
|
|
16
16
|
extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "disabled"> {
|
|
17
17
|
ref: React.Ref<HTMLInputElement>;
|
|
18
18
|
inputClassName?: string;
|
|
19
|
+
shouldShowSelectedOptions?: boolean;
|
|
19
20
|
value?: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
23
|
-
({ inputClassName, ...rest }, ref) => {
|
|
24
|
+
({ inputClassName, shouldShowSelectedOptions, ...rest }, ref) => {
|
|
24
25
|
const internalRef = useRef<HTMLInputElement>(null);
|
|
25
26
|
const mergedRefs = useMergeRefs(ref, internalRef);
|
|
26
27
|
const {
|
|
@@ -128,7 +129,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
128
129
|
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
129
130
|
setIsMouseLastUsedInputDevice(false);
|
|
130
131
|
if (e.key === "Backspace") {
|
|
131
|
-
if (value === "") {
|
|
132
|
+
if (value === "" && shouldShowSelectedOptions) {
|
|
132
133
|
const lastSelectedOption =
|
|
133
134
|
selectedOptions[selectedOptions.length - 1];
|
|
134
135
|
if (lastSelectedOption) {
|
|
@@ -191,6 +192,7 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
191
192
|
virtualFocus,
|
|
192
193
|
setValue,
|
|
193
194
|
searchTerm,
|
|
195
|
+
shouldShowSelectedOptions,
|
|
194
196
|
],
|
|
195
197
|
);
|
|
196
198
|
|