@vuu-ui/vuu-popups 0.8.98 → 0.8.99
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.
|
@@ -12,7 +12,6 @@ const useKeyboardNavigation = ({
|
|
|
12
12
|
highlightedIndex: highlightedIndexProp,
|
|
13
13
|
onActivate,
|
|
14
14
|
onHighlight,
|
|
15
|
-
// onKeyDown,
|
|
16
15
|
onCloseMenu,
|
|
17
16
|
onOpenMenu
|
|
18
17
|
}) => {
|
|
@@ -48,7 +47,7 @@ const useKeyboardNavigation = ({
|
|
|
48
47
|
const ignoreFocus = React.useRef(false);
|
|
49
48
|
const setIgnoreFocus = (value) => ignoreFocus.current = value;
|
|
50
49
|
const highlightedIndex = controlledHighlighting ? highlightedIndexProp : highlightedIndexRef.current;
|
|
51
|
-
const
|
|
50
|
+
const navigateChildItems = React.useCallback(
|
|
52
51
|
(e) => {
|
|
53
52
|
const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);
|
|
54
53
|
if (nextIdx !== highlightedIndexRef.current) {
|
|
@@ -63,7 +62,7 @@ const useKeyboardNavigation = ({
|
|
|
63
62
|
e.preventDefault();
|
|
64
63
|
e.stopPropagation();
|
|
65
64
|
keyBoardNavigation.current = true;
|
|
66
|
-
|
|
65
|
+
navigateChildItems(e);
|
|
67
66
|
} else if ((e.key === "ArrowRight" || e.key === "Enter") && utils.hasPopup(e.target, highlightedIndex)) {
|
|
68
67
|
const menuEl = e.target;
|
|
69
68
|
const menuItemEl = menuEl.querySelector(
|
|
@@ -82,13 +81,7 @@ const useKeyboardNavigation = ({
|
|
|
82
81
|
onCloseMenu(e, "tab-away");
|
|
83
82
|
}
|
|
84
83
|
},
|
|
85
|
-
[
|
|
86
|
-
highlightedIndex,
|
|
87
|
-
navigateChildldItems,
|
|
88
|
-
onActivate,
|
|
89
|
-
onCloseMenu,
|
|
90
|
-
onOpenMenu
|
|
91
|
-
]
|
|
84
|
+
[highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu]
|
|
92
85
|
);
|
|
93
86
|
const listProps = React.useMemo(
|
|
94
87
|
() => ({
|
|
@@ -121,7 +114,6 @@ const useKeyboardNavigation = ({
|
|
|
121
114
|
controlledHighlighting,
|
|
122
115
|
highlightedIndex,
|
|
123
116
|
setHighlightedIndex,
|
|
124
|
-
// keyBoardNavigation,
|
|
125
117
|
listProps,
|
|
126
118
|
setIgnoreFocus
|
|
127
119
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n // onKeyDown,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildldItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildldItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [\n highlightedIndex,\n navigateChildldItems,\n onActivate,\n onCloseMenu,\n onOpenMenu,\n ],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n // label === 'ParsedInput' && console.log(`%c[useKeyboardNavigationHook]<${label}> onMouseLeave`,'color:brown')\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n // keyBoardNavigation,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":["isValidNumber","useRef","useState","useCallback","isNavigationKey","hasPopup","isRoot","useMemo"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":["isValidNumber","useRef","useState","useCallback","isNavigationKey","hasPopup","isRoot","useMemo"],"mappings":"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"}
|
|
@@ -10,7 +10,6 @@ const useKeyboardNavigation = ({
|
|
|
10
10
|
highlightedIndex: highlightedIndexProp,
|
|
11
11
|
onActivate,
|
|
12
12
|
onHighlight,
|
|
13
|
-
// onKeyDown,
|
|
14
13
|
onCloseMenu,
|
|
15
14
|
onOpenMenu
|
|
16
15
|
}) => {
|
|
@@ -46,7 +45,7 @@ const useKeyboardNavigation = ({
|
|
|
46
45
|
const ignoreFocus = useRef(false);
|
|
47
46
|
const setIgnoreFocus = (value) => ignoreFocus.current = value;
|
|
48
47
|
const highlightedIndex = controlledHighlighting ? highlightedIndexProp : highlightedIndexRef.current;
|
|
49
|
-
const
|
|
48
|
+
const navigateChildItems = useCallback(
|
|
50
49
|
(e) => {
|
|
51
50
|
const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);
|
|
52
51
|
if (nextIdx !== highlightedIndexRef.current) {
|
|
@@ -61,7 +60,7 @@ const useKeyboardNavigation = ({
|
|
|
61
60
|
e.preventDefault();
|
|
62
61
|
e.stopPropagation();
|
|
63
62
|
keyBoardNavigation.current = true;
|
|
64
|
-
|
|
63
|
+
navigateChildItems(e);
|
|
65
64
|
} else if ((e.key === "ArrowRight" || e.key === "Enter") && hasPopup(e.target, highlightedIndex)) {
|
|
66
65
|
const menuEl = e.target;
|
|
67
66
|
const menuItemEl = menuEl.querySelector(
|
|
@@ -80,13 +79,7 @@ const useKeyboardNavigation = ({
|
|
|
80
79
|
onCloseMenu(e, "tab-away");
|
|
81
80
|
}
|
|
82
81
|
},
|
|
83
|
-
[
|
|
84
|
-
highlightedIndex,
|
|
85
|
-
navigateChildldItems,
|
|
86
|
-
onActivate,
|
|
87
|
-
onCloseMenu,
|
|
88
|
-
onOpenMenu
|
|
89
|
-
]
|
|
82
|
+
[highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu]
|
|
90
83
|
);
|
|
91
84
|
const listProps = useMemo(
|
|
92
85
|
() => ({
|
|
@@ -119,7 +112,6 @@ const useKeyboardNavigation = ({
|
|
|
119
112
|
controlledHighlighting,
|
|
120
113
|
highlightedIndex,
|
|
121
114
|
setHighlightedIndex,
|
|
122
|
-
// keyBoardNavigation,
|
|
123
115
|
listProps,
|
|
124
116
|
setIgnoreFocus
|
|
125
117
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n // onKeyDown,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildldItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildldItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [\n highlightedIndex,\n navigateChildldItems,\n onActivate,\n onCloseMenu,\n onOpenMenu,\n ],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n // label === 'ParsedInput' && console.log(`%c[useKeyboardNavigationHook]<${label}> onMouseLeave`,'color:brown')\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n // keyBoardNavigation,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-keyboard-navigation.js","sources":["../../src/menu/use-keyboard-navigation.ts"],"sourcesContent":["import {\n FocusEvent,\n KeyboardEvent,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { hasPopup, isRoot } from \"./utils\";\nimport { isNavigationKey } from \"./key-code\";\nimport { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { MenuOpenHandler } from \"./MenuList\";\n\nexport type MenuCloseReason = \"tab-away\" | \"close-child-menu\";\n\nexport type MenuCloseHandler = (\n evt: KeyboardEvent,\n reason: MenuCloseReason,\n) => void;\n\nexport interface KeyboardNavigationProps {\n autoHighlightFirstItem?: boolean;\n count: number;\n defaultHighlightedIdx?: number;\n highlightedIndex?: number;\n onActivate: (idx: number) => void;\n onHighlight?: (idx: number) => void;\n onCloseMenu: MenuCloseHandler;\n onOpenMenu?: MenuOpenHandler;\n}\n\nexport interface KeyboardHookListProps {\n // onBlur: (evt: FocusEvent) => void;\n onFocus: (evt: FocusEvent) => void;\n onKeyDown: (evt: KeyboardEvent) => void;\n onMouseDownCapture: () => void;\n onMouseMove: () => void;\n onMouseLeave: () => void;\n}\n\nexport interface NavigationHookResult {\n focusVisible: number;\n controlledHighlighting: boolean;\n highlightedIndex: number;\n setHighlightedIndex: (idx: number) => void;\n // keyboardNavigation: RefObject<boolean>;\n listProps: KeyboardHookListProps;\n setIgnoreFocus: (ignoreFocus: boolean) => void;\n}\n\n// we need a way to set highlightedIdx when selection changes\nexport const useKeyboardNavigation = ({\n autoHighlightFirstItem = false,\n count,\n defaultHighlightedIdx,\n highlightedIndex: highlightedIndexProp,\n onActivate,\n onHighlight,\n onCloseMenu,\n onOpenMenu,\n}: KeyboardNavigationProps): NavigationHookResult => {\n if (\n isValidNumber(highlightedIndexProp) &&\n isValidNumber(defaultHighlightedIdx)\n ) {\n throw Error(\n \"useKeyboardNavigation do not pass values for both highlightedIndex and defaultHighlightedIdx\",\n );\n }\n\n const controlledHighlighting = isValidNumber(highlightedIndexProp);\n const highlightedIndexRef = useRef(\n defaultHighlightedIdx ??\n highlightedIndexProp ??\n (autoHighlightFirstItem ? 0 : -1),\n );\n const [, forceRender] = useState<unknown>(null);\n\n const setHighlightedIdx = useCallback(\n (idx) => {\n highlightedIndexRef.current = idx;\n onHighlight?.(idx);\n forceRender({});\n },\n [onHighlight],\n );\n\n const setHighlightedIndex = useCallback(\n (idx) => {\n if (idx !== highlightedIndexRef.current) {\n if (!controlledHighlighting) {\n setHighlightedIdx(idx);\n }\n }\n },\n [controlledHighlighting, setHighlightedIdx],\n );\n\n // does this belong here or should it be a method passed in?\n const keyBoardNavigation = useRef(true);\n const ignoreFocus = useRef(false);\n const setIgnoreFocus = (value: boolean) => (ignoreFocus.current = value);\n\n const highlightedIndex = controlledHighlighting\n ? highlightedIndexProp\n : highlightedIndexRef.current;\n\n const navigateChildItems = useCallback(\n (e: KeyboardEvent) => {\n const nextIdx = nextItemIdx(count, e.key, highlightedIndexRef.current);\n if (nextIdx !== highlightedIndexRef.current) {\n setHighlightedIndex(nextIdx);\n }\n },\n [count, setHighlightedIndex],\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (isNavigationKey(e)) {\n e.preventDefault();\n e.stopPropagation();\n keyBoardNavigation.current = true;\n navigateChildItems(e);\n } else if (\n (e.key === \"ArrowRight\" || e.key === \"Enter\") &&\n hasPopup(e.target as HTMLElement, highlightedIndex)\n ) {\n const menuEl = e.target as HTMLElement;\n const menuItemEl = menuEl.querySelector(\n `:scope > [data-index='${highlightedIndex}']`,\n ) as HTMLElement;\n\n if (menuItemEl) {\n onOpenMenu?.(menuItemEl, true);\n }\n } else if (e.key === \"ArrowLeft\" && !isRoot(e.target as HTMLElement)) {\n onCloseMenu(e, \"close-child-menu\");\n } else if (e.key === \"Enter\") {\n e.preventDefault();\n e.stopPropagation();\n onActivate && onActivate(highlightedIndex);\n } else if (e.key === \"Tab\") {\n onCloseMenu(e, \"tab-away\");\n }\n },\n [highlightedIndex, navigateChildItems, onActivate, onCloseMenu, onOpenMenu],\n );\n\n const listProps: KeyboardHookListProps = useMemo(\n () => ({\n onFocus: () => {\n if (highlightedIndex === -1) {\n setHighlightedIdx(0);\n }\n },\n onKeyDown: handleKeyDown,\n onMouseDownCapture: () => {\n keyBoardNavigation.current = false;\n setIgnoreFocus(true);\n },\n\n // onMouseEnter would seem less expensive but it misses some cases\n onMouseMove: () => {\n if (keyBoardNavigation.current) {\n keyBoardNavigation.current = false;\n }\n },\n onMouseLeave: () => {\n keyBoardNavigation.current = true;\n setIgnoreFocus(false);\n setHighlightedIndex(-1);\n },\n }),\n [handleKeyDown, highlightedIndex, setHighlightedIdx, setHighlightedIndex],\n );\n\n return {\n focusVisible: keyBoardNavigation.current ? highlightedIndex : -1,\n controlledHighlighting,\n highlightedIndex,\n setHighlightedIndex: setHighlightedIndex,\n listProps,\n setIgnoreFocus,\n };\n};\n\n// need to be able to accommodate disabled items\nfunction nextItemIdx(count: number, key: string, idx: number) {\n if (key === \"ArrowUp\") {\n if (idx > 0) {\n return idx - 1;\n } else {\n return idx;\n }\n } else {\n if (idx === null) {\n return 0;\n } else if (idx === count - 1) {\n return idx;\n } else {\n return idx + 1;\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.8.
|
|
2
|
+
"version": "0.8.99",
|
|
3
3
|
"description": "VUU popup components - Context Menu, Dialog etc",
|
|
4
4
|
"author": "heswell",
|
|
5
5
|
"license": "Apache-2.0",
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"@salt-ds/core": "1.34.0",
|
|
8
8
|
"@salt-ds/styles": "0.2.1",
|
|
9
9
|
"@salt-ds/window": "0.1.1",
|
|
10
|
-
"@vuu-ui/vuu-data-types": "0.8.
|
|
11
|
-
"@vuu-ui/vuu-layout": "0.8.
|
|
12
|
-
"@vuu-ui/vuu-utils": "0.8.
|
|
13
|
-
"@vuu-ui/vuu-ui-controls": "0.8.
|
|
10
|
+
"@vuu-ui/vuu-data-types": "0.8.99",
|
|
11
|
+
"@vuu-ui/vuu-layout": "0.8.99",
|
|
12
|
+
"@vuu-ui/vuu-utils": "0.8.99",
|
|
13
|
+
"@vuu-ui/vuu-ui-controls": "0.8.99"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
16
|
"clsx": "^2.0.0",
|