@elliemae/ds-hooks-keyboard-navigation 3.34.0 → 3.35.0-rc.1

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.
@@ -34,9 +34,10 @@ __export(DSHooksKeyboardNavigation_exports, {
34
34
  module.exports = __toCommonJS(DSHooksKeyboardNavigation_exports);
35
35
  var React = __toESM(require("react"));
36
36
  var import_react = require("react");
37
- var import_ds_utilities = require("@elliemae/ds-utilities");
37
+ var import_ds_hooks_on_blur_out = require("@elliemae/ds-hooks-on-blur-out");
38
38
  var import_ds_props_helpers = require("@elliemae/ds-props-helpers");
39
39
  var import_react_desc_prop_types2 = require("./react-desc-prop-types.js");
40
+ var import_utils = require("./utils.js");
40
41
  const defaultFocusCriteria = () => true;
41
42
  const useKeyboardNavigation = (props) => {
42
43
  const {
@@ -44,21 +45,24 @@ const useKeyboardNavigation = (props) => {
44
45
  direction = "agnostic",
45
46
  disableRoving = false,
46
47
  focusCriteria = defaultFocusCriteria,
47
- onKeyDown: userOnKeyDown
48
+ onKeyDown: userOnKeyDown,
49
+ focusedOption,
50
+ setFocusedOption
48
51
  } = props;
49
- const [focusedOption, setFocusedOption] = (0, import_react.useState)(null);
50
52
  const previousFocusedOption = (0, import_react.useRef)(null);
51
53
  const focusedOptionMutable = (0, import_react.useRef)(null);
52
54
  const config = (0, import_react.useMemo)(
53
55
  () => ({
54
56
  onBlur: () => {
55
- previousFocusedOption.current = focusedOption;
56
- setFocusedOption(null);
57
+ if (options.includes(focusedOption)) {
58
+ previousFocusedOption.current = focusedOption;
59
+ setFocusedOption(null);
60
+ }
57
61
  }
58
62
  }),
59
- [focusedOption]
63
+ [focusedOption, options, setFocusedOption]
60
64
  );
61
- const handleParentOnBlur = (0, import_ds_utilities.useOnBlurOut)(config);
65
+ const handleParentOnBlur = (0, import_ds_hooks_on_blur_out.useOnBlurOut)(config);
62
66
  const prevArrowKeys = (0, import_react.useMemo)(() => {
63
67
  if (direction === "vertical")
64
68
  return ["ArrowUp"];
@@ -81,28 +85,31 @@ const useKeyboardNavigation = (props) => {
81
85
  const index = options.findIndex((option) => option === focusedOptionMutable.current);
82
86
  if (prevArrowKeys.includes(key)) {
83
87
  e.preventDefault();
84
- const previous = (0, import_ds_utilities.findInCircularList)(options, index, focusCriteria, -1);
88
+ const previous = (0, import_utils.findInCircularList)(options, index, focusCriteria, -1);
85
89
  setFocusedOption(options[previous]);
86
90
  } else if (nextArrowKeys.includes(key)) {
87
91
  e.preventDefault();
88
- const next = (0, import_ds_utilities.findInCircularList)(options, index, focusCriteria, 1);
92
+ const next = (0, import_utils.findInCircularList)(options, index, focusCriteria, 1);
89
93
  setFocusedOption(options[next]);
90
94
  }
91
95
  },
92
- [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria]
96
+ [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria, setFocusedOption]
93
97
  );
94
98
  const getItemProps = (0, import_react.useCallback)(
95
99
  (dsId) => ({
96
100
  innerRef: (el) => {
97
101
  if (el && dsId === focusedOption && focusedOptionMutable.current !== dsId) {
98
102
  el?.focus();
99
- focusedOptionMutable.current = dsId;
100
103
  }
101
104
  },
102
- onFocus: () => setFocusedOption(dsId),
103
- tabIndex: disableRoving || dsId === focusedOptionMutable.current || previousFocusedOption.current === dsId && focusedOptionMutable.current === null || dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null ? 0 : -1
105
+ onFocus: () => {
106
+ setFocusedOption(dsId);
107
+ focusedOptionMutable.current = dsId;
108
+ },
109
+ tabIndex: disableRoving || dsId === focusedOption || focusedOptionMutable.current === dsId && (focusedOption === null || !options.includes(focusedOption)) || // (previousFocusedOption.current === dsId && focusedOption === null) ||
110
+ dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null ? 0 : -1
104
111
  }),
105
- [disableRoving, focusedOption, options]
112
+ [disableRoving, focusedOption, options, setFocusedOption]
106
113
  );
107
114
  const getWrapperProps = (0, import_react.useCallback)(
108
115
  () => ({
@@ -113,7 +120,7 @@ const useKeyboardNavigation = (props) => {
113
120
  );
114
121
  return (0, import_react.useMemo)(
115
122
  () => ({ getWrapperProps, getItemProps, focusedOption, setFocusedOption }),
116
- [focusedOption, getWrapperProps, getItemProps]
123
+ [getWrapperProps, getItemProps, focusedOption, setFocusedOption]
117
124
  );
118
125
  };
119
126
  const UseKeyboardNavigationWithSchema = (0, import_ds_props_helpers.describe)(useKeyboardNavigation);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/DSHooksKeyboardNavigation.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import { useCallback, useMemo, useRef, useState } from 'react';\nimport { useOnBlurOut, findInCircularList } from '@elliemae/ds-utilities';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSHooksKeyboardNaviationT } from './react-desc-prop-types.js';\nimport { propTypes } from './react-desc-prop-types.js';\nconst defaultFocusCriteria = () => true;\nconst useKeyboardNavigation = (props: DSHooksKeyboardNaviationT.Props) => {\n const {\n options,\n direction = 'agnostic',\n disableRoving = false,\n focusCriteria = defaultFocusCriteria,\n onKeyDown: userOnKeyDown,\n } = props;\n const [focusedOption, setFocusedOption] = useState<string | null>(null);\n const previousFocusedOption = useRef<string | null>(null);\n const focusedOptionMutable = useRef<string | null>(null);\n\n const config = useMemo(\n () => ({\n onBlur: () => {\n previousFocusedOption.current = focusedOption;\n setFocusedOption(null);\n },\n }),\n [focusedOption],\n );\n const handleParentOnBlur = useOnBlurOut(config);\n\n const prevArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowUp'];\n if (direction === 'horizontal') return ['ArrowLeft'];\n return ['ArrowUp', 'ArrowLeft'];\n }, [direction]);\n const nextArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowDown'];\n if (direction === 'horizontal') return ['ArrowRight'];\n return ['ArrowDown', 'ArrowRight'];\n }, [direction]);\n const onKeyDown: React.KeyboardEventHandler = useCallback(\n (e) => {\n if (userOnKeyDown) userOnKeyDown(e);\n const { key } = e;\n const index = options.findIndex((option) => option === focusedOptionMutable.current);\n if (prevArrowKeys.includes(key)) {\n e.preventDefault();\n const previous = findInCircularList(options, index, focusCriteria, -1);\n setFocusedOption(options[previous]);\n } else if (nextArrowKeys.includes(key)) {\n e.preventDefault();\n const next = findInCircularList(options, index, focusCriteria, 1);\n setFocusedOption(options[next]);\n }\n },\n [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria],\n );\n\n const getItemProps = useCallback(\n (dsId: string) => ({\n innerRef: (el: HTMLElement | null) => {\n if (el && dsId === focusedOption && focusedOptionMutable.current !== dsId) {\n el?.focus();\n focusedOptionMutable.current = dsId;\n }\n },\n onFocus: () => setFocusedOption(dsId),\n tabIndex: (disableRoving ||\n dsId === focusedOptionMutable.current ||\n (previousFocusedOption.current === dsId && focusedOptionMutable.current === null) ||\n (dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null)\n ? 0\n : -1) as TypescriptHelpersT.WCAGTabIndex,\n }),\n [disableRoving, focusedOption, options],\n );\n\n const getWrapperProps = useCallback(\n () => ({\n onKeyDown,\n onBlur: handleParentOnBlur,\n }),\n [onKeyDown, handleParentOnBlur],\n );\n\n return useMemo(\n () => ({ getWrapperProps, getItemProps, focusedOption, setFocusedOption }),\n [focusedOption, getWrapperProps, getItemProps],\n );\n};\n\nconst UseKeyboardNavigationWithSchema = describe(useKeyboardNavigation);\nUseKeyboardNavigationWithSchema.propTypes = propTypes;\n\nexport { useKeyboardNavigation, UseKeyboardNavigationWithSchema };\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAuD;AACvD,0BAAiD;AACjD,8BAAyB;AAGzB,IAAAA,gCAA0B;AAC1B,MAAM,uBAAuB,MAAM;AACnC,MAAM,wBAAwB,CAAC,UAA2C;AACxE,QAAM;AAAA,IACJ;AAAA,IACA,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,WAAW;AAAA,EACb,IAAI;AACJ,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAwB,IAAI;AACtE,QAAM,4BAAwB,qBAAsB,IAAI;AACxD,QAAM,2BAAuB,qBAAsB,IAAI;AAEvD,QAAM,aAAS;AAAA,IACb,OAAO;AAAA,MACL,QAAQ,MAAM;AACZ,8BAAsB,UAAU;AAChC,yBAAiB,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AACA,QAAM,yBAAqB,kCAAa,MAAM;AAE9C,QAAM,oBAAgB,sBAAQ,MAAM;AAClC,QAAI,cAAc;AAAY,aAAO,CAAC,SAAS;AAC/C,QAAI,cAAc;AAAc,aAAO,CAAC,WAAW;AACnD,WAAO,CAAC,WAAW,WAAW;AAAA,EAChC,GAAG,CAAC,SAAS,CAAC;AACd,QAAM,oBAAgB,sBAAQ,MAAM;AAClC,QAAI,cAAc;AAAY,aAAO,CAAC,WAAW;AACjD,QAAI,cAAc;AAAc,aAAO,CAAC,YAAY;AACpD,WAAO,CAAC,aAAa,YAAY;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AACd,QAAM,gBAAwC;AAAA,IAC5C,CAAC,MAAM;AACL,UAAI;AAAe,sBAAc,CAAC;AAClC,YAAM,EAAE,IAAI,IAAI;AAChB,YAAM,QAAQ,QAAQ,UAAU,CAAC,WAAW,WAAW,qBAAqB,OAAO;AACnF,UAAI,cAAc,SAAS,GAAG,GAAG;AAC/B,UAAE,eAAe;AACjB,cAAM,eAAW,wCAAmB,SAAS,OAAO,eAAe,EAAE;AACrE,yBAAiB,QAAQ,QAAQ,CAAC;AAAA,MACpC,WAAW,cAAc,SAAS,GAAG,GAAG;AACtC,UAAE,eAAe;AACjB,cAAM,WAAO,wCAAmB,SAAS,OAAO,eAAe,CAAC;AAChE,yBAAiB,QAAQ,IAAI,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,IACA,CAAC,eAAe,SAAS,eAAe,eAAe,aAAa;AAAA,EACtE;AAEA,QAAM,mBAAe;AAAA,IACnB,CAAC,UAAkB;AAAA,MACjB,UAAU,CAAC,OAA2B;AACpC,YAAI,MAAM,SAAS,iBAAiB,qBAAqB,YAAY,MAAM;AACzE,cAAI,MAAM;AACV,+BAAqB,UAAU;AAAA,QACjC;AAAA,MACF;AAAA,MACA,SAAS,MAAM,iBAAiB,IAAI;AAAA,MACpC,UAAW,iBACX,SAAS,qBAAqB,WAC7B,sBAAsB,YAAY,QAAQ,qBAAqB,YAAY,QAC3E,SAAS,QAAQ,CAAC,KAAK,qBAAqB,YAAY,QAAQ,sBAAsB,YAAY,OAC/F,IACA;AAAA,IACN;AAAA,IACA,CAAC,eAAe,eAAe,OAAO;AAAA,EACxC;AAEA,QAAM,sBAAkB;AAAA,IACtB,OAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,CAAC,WAAW,kBAAkB;AAAA,EAChC;AAEA,aAAO;AAAA,IACL,OAAO,EAAE,iBAAiB,cAAc,eAAe,iBAAiB;AAAA,IACxE,CAAC,eAAe,iBAAiB,YAAY;AAAA,EAC/C;AACF;AAEA,MAAM,sCAAkC,kCAAS,qBAAqB;AACtE,gCAAgC,YAAY;",
4
+ "sourcesContent": ["/* eslint-disable complexity */\nimport { useCallback, useMemo, useRef } from 'react';\nimport { useOnBlurOut, type DSHooksUseOBlurOutT } from '@elliemae/ds-hooks-on-blur-out';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSHooksKeyboardNaviationT } from './react-desc-prop-types.js';\nimport { propTypes } from './react-desc-prop-types.js';\nimport { findInCircularList } from './utils.js';\n\nconst defaultFocusCriteria = () => true;\nconst useKeyboardNavigation = (props: DSHooksKeyboardNaviationT.Props) => {\n const {\n options,\n direction = 'agnostic',\n disableRoving = false,\n focusCriteria = defaultFocusCriteria,\n onKeyDown: userOnKeyDown,\n focusedOption,\n setFocusedOption,\n } = props;\n const previousFocusedOption = useRef<string | null>(null);\n const focusedOptionMutable = useRef<string | null>(null);\n\n const config: {\n onBlur: DSHooksUseOBlurOutT.OnBlurCb;\n } = useMemo(\n () => ({\n onBlur: () => {\n // if (e.currentTarget !== e.target) return;\n if (options.includes(focusedOption as string)) {\n previousFocusedOption.current = focusedOption;\n setFocusedOption(null);\n }\n },\n }),\n [focusedOption, options, setFocusedOption],\n );\n const handleParentOnBlur = useOnBlurOut(config);\n\n const prevArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowUp'];\n if (direction === 'horizontal') return ['ArrowLeft'];\n return ['ArrowUp', 'ArrowLeft'];\n }, [direction]);\n const nextArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowDown'];\n if (direction === 'horizontal') return ['ArrowRight'];\n return ['ArrowDown', 'ArrowRight'];\n }, [direction]);\n const onKeyDown: React.KeyboardEventHandler = useCallback(\n (e) => {\n if (userOnKeyDown) userOnKeyDown(e);\n const { key } = e;\n const index = options.findIndex((option) => option === focusedOptionMutable.current);\n if (prevArrowKeys.includes(key)) {\n e.preventDefault();\n const previous = findInCircularList(options, index, focusCriteria, -1);\n setFocusedOption(options[previous]);\n } else if (nextArrowKeys.includes(key)) {\n e.preventDefault();\n const next = findInCircularList(options, index, focusCriteria, 1);\n\n setFocusedOption(options[next]);\n }\n },\n [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria, setFocusedOption],\n );\n\n const getItemProps = useCallback(\n (dsId: string) => ({\n innerRef: (el: HTMLElement | null) => {\n if (el && dsId === focusedOption && focusedOptionMutable.current !== dsId) {\n el?.focus();\n }\n },\n onFocus: () => {\n setFocusedOption(dsId);\n focusedOptionMutable.current = dsId;\n },\n tabIndex: (disableRoving ||\n dsId === focusedOption ||\n (focusedOptionMutable.current === dsId && (focusedOption === null || !options.includes(focusedOption))) ||\n // (previousFocusedOption.current === dsId && focusedOption === null) ||\n (dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null)\n ? 0\n : -1) as TypescriptHelpersT.WCAGTabIndex,\n }),\n [disableRoving, focusedOption, options, setFocusedOption],\n );\n\n const getWrapperProps = useCallback(\n () => ({\n onKeyDown,\n onBlur: handleParentOnBlur,\n }),\n [onKeyDown, handleParentOnBlur],\n );\n\n return useMemo(\n () => ({ getWrapperProps, getItemProps, focusedOption, setFocusedOption }),\n [getWrapperProps, getItemProps, focusedOption, setFocusedOption],\n );\n};\n\nconst UseKeyboardNavigationWithSchema = describe(useKeyboardNavigation);\nUseKeyboardNavigationWithSchema.propTypes = propTypes;\n\nexport { useKeyboardNavigation, UseKeyboardNavigationWithSchema };\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_react_desc_prop_types"]
7
7
  }
@@ -32,18 +32,18 @@ __export(react_desc_prop_types_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(react_desc_prop_types_exports);
34
34
  var React = __toESM(require("react"));
35
- var import_ds_utilities = require("@elliemae/ds-utilities");
35
+ var import_ds_props_helpers = require("@elliemae/ds-props-helpers");
36
36
  const propTypes = {
37
- options: import_ds_utilities.PropTypes.arrayOf(import_ds_utilities.PropTypes.string).description("Array of strings to be items ids.").isRequired,
38
- direction: import_ds_utilities.PropTypes.oneOf(["horizontal", "vertical", "agnostic"]).description(
37
+ options: import_ds_props_helpers.PropTypes.arrayOf(import_ds_props_helpers.PropTypes.string).description("Array of strings to be items ids.").isRequired,
38
+ direction: import_ds_props_helpers.PropTypes.oneOf(["horizontal", "vertical", "agnostic"]).description(
39
39
  `The direction of the navigation.
40
40
  If horizontal, "Left" triggers "previous" and "Right" triggers "next".
41
41
  If vertical, "Up" triggers "previous" and "Down" triggers "next".
42
42
  If agnostic, "Left" and "Up" trigger "previous" and "Right" and "Down" trigger "next".
43
43
  Defaults to "agnostic".`
44
44
  ).defaultValue("agnostic"),
45
- disableRoving: import_ds_utilities.PropTypes.bool.description("If true, the roving tab index will not be applied to the items.").defaultValue(false),
46
- focusCriteria: import_ds_utilities.PropTypes.func.description(
45
+ disableRoving: import_ds_props_helpers.PropTypes.bool.description("If true, the roving tab index will not be applied to the items.").defaultValue(false),
46
+ focusCriteria: import_ds_props_helpers.PropTypes.func.description(
47
47
  "Function that returns a boolean to determine if the element should be focused.When not provided, all elements will be accounted as focusable."
48
48
  )
49
49
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/react-desc-prop-types.ts", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-empty-interface */\n\nimport type React from 'react';\nimport { PropTypes } from '@elliemae/ds-utilities';\nimport { type useKeyboardNavigation } from './DSHooksKeyboardNavigation.js';\nexport declare namespace DSHooksKeyboardNaviationT {\n interface Props {\n options: string[];\n direction?: 'horizontal' | 'vertical' | 'agnostic';\n disableRoving?: boolean;\n focusCriteria?: (dsId: string) => boolean;\n onKeyDown?: React.KeyboardEventHandler;\n }\n\n type UseKeyboardNavigationReturnType = ReturnType<typeof useKeyboardNavigation>;\n type ItemPropsT = ReturnType<UseKeyboardNavigationReturnType['getItemProps']>;\n type WrapperPropsT = ReturnType<UseKeyboardNavigationReturnType['getWrapperProps']>;\n}\n\nexport const propTypes = {\n options: PropTypes.arrayOf(PropTypes.string).description('Array of strings to be items ids.').isRequired,\n direction: PropTypes.oneOf(['horizontal', 'vertical', 'agnostic'])\n .description(\n `The direction of the navigation.\n If horizontal, \"Left\" triggers \"previous\" and \"Right\" triggers \"next\".\n If vertical, \"Up\" triggers \"previous\" and \"Down\" triggers \"next\".\n If agnostic, \"Left\" and \"Up\" trigger \"previous\" and \"Right\" and \"Down\" trigger \"next\".\nDefaults to \"agnostic\".`,\n )\n .defaultValue('agnostic'),\n disableRoving: PropTypes.bool\n .description('If true, the roving tab index will not be applied to the items.')\n .defaultValue(false),\n focusCriteria: PropTypes.func.description(\n 'Function that returns a boolean to determine if the element should be focused.When not provided, all elements will be accounted as focusable.',\n ),\n} as React.WeakValidationMap<unknown>;\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADGvB,0BAA0B;AAgBnB,MAAM,YAAY;AAAA,EACvB,SAAS,8BAAU,QAAQ,8BAAU,MAAM,EAAE,YAAY,mCAAmC,EAAE;AAAA,EAC9F,WAAW,8BAAU,MAAM,CAAC,cAAc,YAAY,UAAU,CAAC,EAC9D;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,aAAa,UAAU;AAAA,EAC1B,eAAe,8BAAU,KACtB,YAAY,iEAAiE,EAC7E,aAAa,KAAK;AAAA,EACrB,eAAe,8BAAU,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-empty-interface */\n\nimport type React from 'react';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nimport { type useKeyboardNavigation } from './DSHooksKeyboardNavigation.js';\nexport declare namespace DSHooksKeyboardNaviationT {\n interface Props {\n options: string[];\n direction?: 'horizontal' | 'vertical' | 'agnostic';\n disableRoving?: boolean;\n focusCriteria?: (dsId: string) => boolean;\n onKeyDown?: React.KeyboardEventHandler;\n focusedOption: string | null;\n setFocusedOption: React.Dispatch<React.SetStateAction<string | null>>;\n }\n\n type UseKeyboardNavigationReturnType = ReturnType<typeof useKeyboardNavigation>;\n type ItemPropsT = ReturnType<UseKeyboardNavigationReturnType['getItemProps']>;\n type WrapperPropsT = ReturnType<UseKeyboardNavigationReturnType['getWrapperProps']>;\n}\n\nexport const propTypes = {\n options: PropTypes.arrayOf(PropTypes.string).description('Array of strings to be items ids.').isRequired,\n direction: PropTypes.oneOf(['horizontal', 'vertical', 'agnostic'])\n .description(\n `The direction of the navigation.\n If horizontal, \"Left\" triggers \"previous\" and \"Right\" triggers \"next\".\n If vertical, \"Up\" triggers \"previous\" and \"Down\" triggers \"next\".\n If agnostic, \"Left\" and \"Up\" trigger \"previous\" and \"Right\" and \"Down\" trigger \"next\".\nDefaults to \"agnostic\".`,\n )\n .defaultValue('agnostic'),\n disableRoving: PropTypes.bool\n .description('If true, the roving tab index will not be applied to the items.')\n .defaultValue(false),\n focusCriteria: PropTypes.func.description(\n 'Function that returns a boolean to determine if the element should be focused.When not provided, all elements will be accounted as focusable.',\n ),\n} as React.WeakValidationMap<unknown>;\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADGvB,8BAA0B;AAkBnB,MAAM,YAAY;AAAA,EACvB,SAAS,kCAAU,QAAQ,kCAAU,MAAM,EAAE,YAAY,mCAAmC,EAAE;AAAA,EAC9F,WAAW,kCAAU,MAAM,CAAC,cAAc,YAAY,UAAU,CAAC,EAC9D;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,aAAa,UAAU;AAAA,EAC1B,eAAe,kCAAU,KACtB,YAAY,iEAAiE,EAC7E,aAAa,KAAK;AAAA,EACrB,eAAe,kCAAU,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var utils_exports = {};
30
+ __export(utils_exports, {
31
+ findInCircularList: () => findInCircularList
32
+ });
33
+ module.exports = __toCommonJS(utils_exports);
34
+ var React = __toESM(require("react"));
35
+ const findInCircularList = (list, from, criteria = () => true, step = 1) => {
36
+ for (let i = (from + step + list.length) % list.length; i !== from && from > -1; i = (i + step + list.length) % list.length) {
37
+ if (criteria(list[i]))
38
+ return i;
39
+ }
40
+ return from;
41
+ };
42
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils.ts", "../../../../../scripts/build/transpile/react-shim.js"],
4
+ "sourcesContent": ["export const findInCircularList = <T>(\n list: T[],\n from: number,\n criteria: (item: T) => boolean = () => true,\n step = 1,\n // eslint-disable-next-line max-params\n): number => {\n for (\n let i = (from + step + list.length) % list.length;\n i !== from && from > -1;\n i = (i + step + list.length) % list.length\n ) {\n if (criteria(list[i])) return i;\n }\n\n return from; // return same item\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAhB,MAAM,qBAAqB,CAChC,MACA,MACA,WAAiC,MAAM,MACvC,OAAO,MAEI;AACX,WACM,KAAK,OAAO,OAAO,KAAK,UAAU,KAAK,QAC3C,MAAM,QAAQ,OAAO,IACrB,KAAK,IAAI,OAAO,KAAK,UAAU,KAAK,QACpC;AACA,QAAI,SAAS,KAAK,CAAC,CAAC;AAAG,aAAO;AAAA,EAChC;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -1,8 +1,9 @@
1
1
  import * as React from "react";
2
- import { useCallback, useMemo, useRef, useState } from "react";
3
- import { useOnBlurOut, findInCircularList } from "@elliemae/ds-utilities";
2
+ import { useCallback, useMemo, useRef } from "react";
3
+ import { useOnBlurOut } from "@elliemae/ds-hooks-on-blur-out";
4
4
  import { describe } from "@elliemae/ds-props-helpers";
5
5
  import { propTypes } from "./react-desc-prop-types.js";
6
+ import { findInCircularList } from "./utils.js";
6
7
  const defaultFocusCriteria = () => true;
7
8
  const useKeyboardNavigation = (props) => {
8
9
  const {
@@ -10,19 +11,22 @@ const useKeyboardNavigation = (props) => {
10
11
  direction = "agnostic",
11
12
  disableRoving = false,
12
13
  focusCriteria = defaultFocusCriteria,
13
- onKeyDown: userOnKeyDown
14
+ onKeyDown: userOnKeyDown,
15
+ focusedOption,
16
+ setFocusedOption
14
17
  } = props;
15
- const [focusedOption, setFocusedOption] = useState(null);
16
18
  const previousFocusedOption = useRef(null);
17
19
  const focusedOptionMutable = useRef(null);
18
20
  const config = useMemo(
19
21
  () => ({
20
22
  onBlur: () => {
21
- previousFocusedOption.current = focusedOption;
22
- setFocusedOption(null);
23
+ if (options.includes(focusedOption)) {
24
+ previousFocusedOption.current = focusedOption;
25
+ setFocusedOption(null);
26
+ }
23
27
  }
24
28
  }),
25
- [focusedOption]
29
+ [focusedOption, options, setFocusedOption]
26
30
  );
27
31
  const handleParentOnBlur = useOnBlurOut(config);
28
32
  const prevArrowKeys = useMemo(() => {
@@ -55,20 +59,23 @@ const useKeyboardNavigation = (props) => {
55
59
  setFocusedOption(options[next]);
56
60
  }
57
61
  },
58
- [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria]
62
+ [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria, setFocusedOption]
59
63
  );
60
64
  const getItemProps = useCallback(
61
65
  (dsId) => ({
62
66
  innerRef: (el) => {
63
67
  if (el && dsId === focusedOption && focusedOptionMutable.current !== dsId) {
64
68
  el?.focus();
65
- focusedOptionMutable.current = dsId;
66
69
  }
67
70
  },
68
- onFocus: () => setFocusedOption(dsId),
69
- tabIndex: disableRoving || dsId === focusedOptionMutable.current || previousFocusedOption.current === dsId && focusedOptionMutable.current === null || dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null ? 0 : -1
71
+ onFocus: () => {
72
+ setFocusedOption(dsId);
73
+ focusedOptionMutable.current = dsId;
74
+ },
75
+ tabIndex: disableRoving || dsId === focusedOption || focusedOptionMutable.current === dsId && (focusedOption === null || !options.includes(focusedOption)) || // (previousFocusedOption.current === dsId && focusedOption === null) ||
76
+ dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null ? 0 : -1
70
77
  }),
71
- [disableRoving, focusedOption, options]
78
+ [disableRoving, focusedOption, options, setFocusedOption]
72
79
  );
73
80
  const getWrapperProps = useCallback(
74
81
  () => ({
@@ -79,7 +86,7 @@ const useKeyboardNavigation = (props) => {
79
86
  );
80
87
  return useMemo(
81
88
  () => ({ getWrapperProps, getItemProps, focusedOption, setFocusedOption }),
82
- [focusedOption, getWrapperProps, getItemProps]
89
+ [getWrapperProps, getItemProps, focusedOption, setFocusedOption]
83
90
  );
84
91
  };
85
92
  const UseKeyboardNavigationWithSchema = describe(useKeyboardNavigation);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/DSHooksKeyboardNavigation.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useCallback, useMemo, useRef, useState } from 'react';\nimport { useOnBlurOut, findInCircularList } from '@elliemae/ds-utilities';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSHooksKeyboardNaviationT } from './react-desc-prop-types.js';\nimport { propTypes } from './react-desc-prop-types.js';\nconst defaultFocusCriteria = () => true;\nconst useKeyboardNavigation = (props: DSHooksKeyboardNaviationT.Props) => {\n const {\n options,\n direction = 'agnostic',\n disableRoving = false,\n focusCriteria = defaultFocusCriteria,\n onKeyDown: userOnKeyDown,\n } = props;\n const [focusedOption, setFocusedOption] = useState<string | null>(null);\n const previousFocusedOption = useRef<string | null>(null);\n const focusedOptionMutable = useRef<string | null>(null);\n\n const config = useMemo(\n () => ({\n onBlur: () => {\n previousFocusedOption.current = focusedOption;\n setFocusedOption(null);\n },\n }),\n [focusedOption],\n );\n const handleParentOnBlur = useOnBlurOut(config);\n\n const prevArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowUp'];\n if (direction === 'horizontal') return ['ArrowLeft'];\n return ['ArrowUp', 'ArrowLeft'];\n }, [direction]);\n const nextArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowDown'];\n if (direction === 'horizontal') return ['ArrowRight'];\n return ['ArrowDown', 'ArrowRight'];\n }, [direction]);\n const onKeyDown: React.KeyboardEventHandler = useCallback(\n (e) => {\n if (userOnKeyDown) userOnKeyDown(e);\n const { key } = e;\n const index = options.findIndex((option) => option === focusedOptionMutable.current);\n if (prevArrowKeys.includes(key)) {\n e.preventDefault();\n const previous = findInCircularList(options, index, focusCriteria, -1);\n setFocusedOption(options[previous]);\n } else if (nextArrowKeys.includes(key)) {\n e.preventDefault();\n const next = findInCircularList(options, index, focusCriteria, 1);\n setFocusedOption(options[next]);\n }\n },\n [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria],\n );\n\n const getItemProps = useCallback(\n (dsId: string) => ({\n innerRef: (el: HTMLElement | null) => {\n if (el && dsId === focusedOption && focusedOptionMutable.current !== dsId) {\n el?.focus();\n focusedOptionMutable.current = dsId;\n }\n },\n onFocus: () => setFocusedOption(dsId),\n tabIndex: (disableRoving ||\n dsId === focusedOptionMutable.current ||\n (previousFocusedOption.current === dsId && focusedOptionMutable.current === null) ||\n (dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null)\n ? 0\n : -1) as TypescriptHelpersT.WCAGTabIndex,\n }),\n [disableRoving, focusedOption, options],\n );\n\n const getWrapperProps = useCallback(\n () => ({\n onKeyDown,\n onBlur: handleParentOnBlur,\n }),\n [onKeyDown, handleParentOnBlur],\n );\n\n return useMemo(\n () => ({ getWrapperProps, getItemProps, focusedOption, setFocusedOption }),\n [focusedOption, getWrapperProps, getItemProps],\n );\n};\n\nconst UseKeyboardNavigationWithSchema = describe(useKeyboardNavigation);\nUseKeyboardNavigationWithSchema.propTypes = propTypes;\n\nexport { useKeyboardNavigation, UseKeyboardNavigationWithSchema };\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,aAAa,SAAS,QAAQ,gBAAgB;AACvD,SAAS,cAAc,0BAA0B;AACjD,SAAS,gBAAgB;AAGzB,SAAS,iBAAiB;AAC1B,MAAM,uBAAuB,MAAM;AACnC,MAAM,wBAAwB,CAAC,UAA2C;AACxE,QAAM;AAAA,IACJ;AAAA,IACA,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,WAAW;AAAA,EACb,IAAI;AACJ,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB,IAAI;AACtE,QAAM,wBAAwB,OAAsB,IAAI;AACxD,QAAM,uBAAuB,OAAsB,IAAI;AAEvD,QAAM,SAAS;AAAA,IACb,OAAO;AAAA,MACL,QAAQ,MAAM;AACZ,8BAAsB,UAAU;AAChC,yBAAiB,IAAI;AAAA,MACvB;AAAA,IACF;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AACA,QAAM,qBAAqB,aAAa,MAAM;AAE9C,QAAM,gBAAgB,QAAQ,MAAM;AAClC,QAAI,cAAc;AAAY,aAAO,CAAC,SAAS;AAC/C,QAAI,cAAc;AAAc,aAAO,CAAC,WAAW;AACnD,WAAO,CAAC,WAAW,WAAW;AAAA,EAChC,GAAG,CAAC,SAAS,CAAC;AACd,QAAM,gBAAgB,QAAQ,MAAM;AAClC,QAAI,cAAc;AAAY,aAAO,CAAC,WAAW;AACjD,QAAI,cAAc;AAAc,aAAO,CAAC,YAAY;AACpD,WAAO,CAAC,aAAa,YAAY;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AACd,QAAM,YAAwC;AAAA,IAC5C,CAAC,MAAM;AACL,UAAI;AAAe,sBAAc,CAAC;AAClC,YAAM,EAAE,IAAI,IAAI;AAChB,YAAM,QAAQ,QAAQ,UAAU,CAAC,WAAW,WAAW,qBAAqB,OAAO;AACnF,UAAI,cAAc,SAAS,GAAG,GAAG;AAC/B,UAAE,eAAe;AACjB,cAAM,WAAW,mBAAmB,SAAS,OAAO,eAAe,EAAE;AACrE,yBAAiB,QAAQ,QAAQ,CAAC;AAAA,MACpC,WAAW,cAAc,SAAS,GAAG,GAAG;AACtC,UAAE,eAAe;AACjB,cAAM,OAAO,mBAAmB,SAAS,OAAO,eAAe,CAAC;AAChE,yBAAiB,QAAQ,IAAI,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,IACA,CAAC,eAAe,SAAS,eAAe,eAAe,aAAa;AAAA,EACtE;AAEA,QAAM,eAAe;AAAA,IACnB,CAAC,UAAkB;AAAA,MACjB,UAAU,CAAC,OAA2B;AACpC,YAAI,MAAM,SAAS,iBAAiB,qBAAqB,YAAY,MAAM;AACzE,cAAI,MAAM;AACV,+BAAqB,UAAU;AAAA,QACjC;AAAA,MACF;AAAA,MACA,SAAS,MAAM,iBAAiB,IAAI;AAAA,MACpC,UAAW,iBACX,SAAS,qBAAqB,WAC7B,sBAAsB,YAAY,QAAQ,qBAAqB,YAAY,QAC3E,SAAS,QAAQ,CAAC,KAAK,qBAAqB,YAAY,QAAQ,sBAAsB,YAAY,OAC/F,IACA;AAAA,IACN;AAAA,IACA,CAAC,eAAe,eAAe,OAAO;AAAA,EACxC;AAEA,QAAM,kBAAkB;AAAA,IACtB,OAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,CAAC,WAAW,kBAAkB;AAAA,EAChC;AAEA,SAAO;AAAA,IACL,OAAO,EAAE,iBAAiB,cAAc,eAAe,iBAAiB;AAAA,IACxE,CAAC,eAAe,iBAAiB,YAAY;AAAA,EAC/C;AACF;AAEA,MAAM,kCAAkC,SAAS,qBAAqB;AACtE,gCAAgC,YAAY;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\nimport { useCallback, useMemo, useRef } from 'react';\nimport { useOnBlurOut, type DSHooksUseOBlurOutT } from '@elliemae/ds-hooks-on-blur-out';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSHooksKeyboardNaviationT } from './react-desc-prop-types.js';\nimport { propTypes } from './react-desc-prop-types.js';\nimport { findInCircularList } from './utils.js';\n\nconst defaultFocusCriteria = () => true;\nconst useKeyboardNavigation = (props: DSHooksKeyboardNaviationT.Props) => {\n const {\n options,\n direction = 'agnostic',\n disableRoving = false,\n focusCriteria = defaultFocusCriteria,\n onKeyDown: userOnKeyDown,\n focusedOption,\n setFocusedOption,\n } = props;\n const previousFocusedOption = useRef<string | null>(null);\n const focusedOptionMutable = useRef<string | null>(null);\n\n const config: {\n onBlur: DSHooksUseOBlurOutT.OnBlurCb;\n } = useMemo(\n () => ({\n onBlur: () => {\n // if (e.currentTarget !== e.target) return;\n if (options.includes(focusedOption as string)) {\n previousFocusedOption.current = focusedOption;\n setFocusedOption(null);\n }\n },\n }),\n [focusedOption, options, setFocusedOption],\n );\n const handleParentOnBlur = useOnBlurOut(config);\n\n const prevArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowUp'];\n if (direction === 'horizontal') return ['ArrowLeft'];\n return ['ArrowUp', 'ArrowLeft'];\n }, [direction]);\n const nextArrowKeys = useMemo(() => {\n if (direction === 'vertical') return ['ArrowDown'];\n if (direction === 'horizontal') return ['ArrowRight'];\n return ['ArrowDown', 'ArrowRight'];\n }, [direction]);\n const onKeyDown: React.KeyboardEventHandler = useCallback(\n (e) => {\n if (userOnKeyDown) userOnKeyDown(e);\n const { key } = e;\n const index = options.findIndex((option) => option === focusedOptionMutable.current);\n if (prevArrowKeys.includes(key)) {\n e.preventDefault();\n const previous = findInCircularList(options, index, focusCriteria, -1);\n setFocusedOption(options[previous]);\n } else if (nextArrowKeys.includes(key)) {\n e.preventDefault();\n const next = findInCircularList(options, index, focusCriteria, 1);\n\n setFocusedOption(options[next]);\n }\n },\n [userOnKeyDown, options, prevArrowKeys, nextArrowKeys, focusCriteria, setFocusedOption],\n );\n\n const getItemProps = useCallback(\n (dsId: string) => ({\n innerRef: (el: HTMLElement | null) => {\n if (el && dsId === focusedOption && focusedOptionMutable.current !== dsId) {\n el?.focus();\n }\n },\n onFocus: () => {\n setFocusedOption(dsId);\n focusedOptionMutable.current = dsId;\n },\n tabIndex: (disableRoving ||\n dsId === focusedOption ||\n (focusedOptionMutable.current === dsId && (focusedOption === null || !options.includes(focusedOption))) ||\n // (previousFocusedOption.current === dsId && focusedOption === null) ||\n (dsId === options[0] && focusedOptionMutable.current === null && previousFocusedOption.current === null)\n ? 0\n : -1) as TypescriptHelpersT.WCAGTabIndex,\n }),\n [disableRoving, focusedOption, options, setFocusedOption],\n );\n\n const getWrapperProps = useCallback(\n () => ({\n onKeyDown,\n onBlur: handleParentOnBlur,\n }),\n [onKeyDown, handleParentOnBlur],\n );\n\n return useMemo(\n () => ({ getWrapperProps, getItemProps, focusedOption, setFocusedOption }),\n [getWrapperProps, getItemProps, focusedOption, setFocusedOption],\n );\n};\n\nconst UseKeyboardNavigationWithSchema = describe(useKeyboardNavigation);\nUseKeyboardNavigationWithSchema.propTypes = propTypes;\n\nexport { useKeyboardNavigation, UseKeyboardNavigationWithSchema };\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { PropTypes } from "@elliemae/ds-utilities";
2
+ import { PropTypes } from "@elliemae/ds-props-helpers";
3
3
  const propTypes = {
4
4
  options: PropTypes.arrayOf(PropTypes.string).description("Array of strings to be items ids.").isRequired,
5
5
  direction: PropTypes.oneOf(["horizontal", "vertical", "agnostic"]).description(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/react-desc-prop-types.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\n\nimport type React from 'react';\nimport { PropTypes } from '@elliemae/ds-utilities';\nimport { type useKeyboardNavigation } from './DSHooksKeyboardNavigation.js';\nexport declare namespace DSHooksKeyboardNaviationT {\n interface Props {\n options: string[];\n direction?: 'horizontal' | 'vertical' | 'agnostic';\n disableRoving?: boolean;\n focusCriteria?: (dsId: string) => boolean;\n onKeyDown?: React.KeyboardEventHandler;\n }\n\n type UseKeyboardNavigationReturnType = ReturnType<typeof useKeyboardNavigation>;\n type ItemPropsT = ReturnType<UseKeyboardNavigationReturnType['getItemProps']>;\n type WrapperPropsT = ReturnType<UseKeyboardNavigationReturnType['getWrapperProps']>;\n}\n\nexport const propTypes = {\n options: PropTypes.arrayOf(PropTypes.string).description('Array of strings to be items ids.').isRequired,\n direction: PropTypes.oneOf(['horizontal', 'vertical', 'agnostic'])\n .description(\n `The direction of the navigation.\n If horizontal, \"Left\" triggers \"previous\" and \"Right\" triggers \"next\".\n If vertical, \"Up\" triggers \"previous\" and \"Down\" triggers \"next\".\n If agnostic, \"Left\" and \"Up\" trigger \"previous\" and \"Right\" and \"Down\" trigger \"next\".\nDefaults to \"agnostic\".`,\n )\n .defaultValue('agnostic'),\n disableRoving: PropTypes.bool\n .description('If true, the roving tab index will not be applied to the items.')\n .defaultValue(false),\n focusCriteria: PropTypes.func.description(\n 'Function that returns a boolean to determine if the element should be focused.When not provided, all elements will be accounted as focusable.',\n ),\n} as React.WeakValidationMap<unknown>;\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACGvB,SAAS,iBAAiB;AAgBnB,MAAM,YAAY;AAAA,EACvB,SAAS,UAAU,QAAQ,UAAU,MAAM,EAAE,YAAY,mCAAmC,EAAE;AAAA,EAC9F,WAAW,UAAU,MAAM,CAAC,cAAc,YAAY,UAAU,CAAC,EAC9D;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,aAAa,UAAU;AAAA,EAC1B,eAAe,UAAU,KACtB,YAAY,iEAAiE,EAC7E,aAAa,KAAK;AAAA,EACrB,eAAe,UAAU,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\n\nimport type React from 'react';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nimport { type useKeyboardNavigation } from './DSHooksKeyboardNavigation.js';\nexport declare namespace DSHooksKeyboardNaviationT {\n interface Props {\n options: string[];\n direction?: 'horizontal' | 'vertical' | 'agnostic';\n disableRoving?: boolean;\n focusCriteria?: (dsId: string) => boolean;\n onKeyDown?: React.KeyboardEventHandler;\n focusedOption: string | null;\n setFocusedOption: React.Dispatch<React.SetStateAction<string | null>>;\n }\n\n type UseKeyboardNavigationReturnType = ReturnType<typeof useKeyboardNavigation>;\n type ItemPropsT = ReturnType<UseKeyboardNavigationReturnType['getItemProps']>;\n type WrapperPropsT = ReturnType<UseKeyboardNavigationReturnType['getWrapperProps']>;\n}\n\nexport const propTypes = {\n options: PropTypes.arrayOf(PropTypes.string).description('Array of strings to be items ids.').isRequired,\n direction: PropTypes.oneOf(['horizontal', 'vertical', 'agnostic'])\n .description(\n `The direction of the navigation.\n If horizontal, \"Left\" triggers \"previous\" and \"Right\" triggers \"next\".\n If vertical, \"Up\" triggers \"previous\" and \"Down\" triggers \"next\".\n If agnostic, \"Left\" and \"Up\" trigger \"previous\" and \"Right\" and \"Down\" trigger \"next\".\nDefaults to \"agnostic\".`,\n )\n .defaultValue('agnostic'),\n disableRoving: PropTypes.bool\n .description('If true, the roving tab index will not be applied to the items.')\n .defaultValue(false),\n focusCriteria: PropTypes.func.description(\n 'Function that returns a boolean to determine if the element should be focused.When not provided, all elements will be accounted as focusable.',\n ),\n} as React.WeakValidationMap<unknown>;\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACGvB,SAAS,iBAAiB;AAkBnB,MAAM,YAAY;AAAA,EACvB,SAAS,UAAU,QAAQ,UAAU,MAAM,EAAE,YAAY,mCAAmC,EAAE;AAAA,EAC9F,WAAW,UAAU,MAAM,CAAC,cAAc,YAAY,UAAU,CAAC,EAC9D;AAAA,IACC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKF,EACC,aAAa,UAAU;AAAA,EAC1B,eAAe,UAAU,KACtB,YAAY,iEAAiE,EAC7E,aAAa,KAAK;AAAA,EACrB,eAAe,UAAU,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ const findInCircularList = (list, from, criteria = () => true, step = 1) => {
3
+ for (let i = (from + step + list.length) % list.length; i !== from && from > -1; i = (i + step + list.length) % list.length) {
4
+ if (criteria(list[i]))
5
+ return i;
6
+ }
7
+ return from;
8
+ };
9
+ export {
10
+ findInCircularList
11
+ };
12
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/utils.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "export const findInCircularList = <T>(\n list: T[],\n from: number,\n criteria: (item: T) => boolean = () => true,\n step = 1,\n // eslint-disable-next-line max-params\n): number => {\n for (\n let i = (from + step + list.length) % list.length;\n i !== from && from > -1;\n i = (i + step + list.length) % list.length\n ) {\n if (criteria(list[i])) return i;\n }\n\n return from; // return same item\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAhB,MAAM,qBAAqB,CAChC,MACA,MACA,WAAiC,MAAM,MACvC,OAAO,MAEI;AACX,WACM,KAAK,OAAO,OAAO,KAAK,UAAU,KAAK,QAC3C,MAAM,QAAQ,OAAO,IACrB,KAAK,IAAI,OAAO,KAAK,UAAU,KAAK,QACpC;AACA,QAAI,SAAS,KAAK,CAAC,CAAC;AAAG,aAAO;AAAA,EAChC;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -1,10 +1,11 @@
1
1
  /// <reference types="react" />
2
+ import { type DSHooksUseOBlurOutT } from '@elliemae/ds-hooks-on-blur-out';
2
3
  import { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';
3
4
  import { type DSHooksKeyboardNaviationT } from './react-desc-prop-types.js';
4
5
  declare const useKeyboardNavigation: (props: DSHooksKeyboardNaviationT.Props) => {
5
6
  getWrapperProps: () => {
6
7
  onKeyDown: import("react").KeyboardEventHandler<Element>;
7
- onBlur: import("@elliemae/ds-utilities").DSHooksUseOBlurOutT.OnBlurCb;
8
+ onBlur: DSHooksUseOBlurOutT.OnBlurCb;
8
9
  };
9
10
  getItemProps: (dsId: string) => {
10
11
  innerRef: (el: HTMLElement | null) => void;
@@ -7,6 +7,8 @@ export declare namespace DSHooksKeyboardNaviationT {
7
7
  disableRoving?: boolean;
8
8
  focusCriteria?: (dsId: string) => boolean;
9
9
  onKeyDown?: React.KeyboardEventHandler;
10
+ focusedOption: string | null;
11
+ setFocusedOption: React.Dispatch<React.SetStateAction<string | null>>;
10
12
  }
11
13
  type UseKeyboardNavigationReturnType = ReturnType<typeof useKeyboardNavigation>;
12
14
  type ItemPropsT = ReturnType<UseKeyboardNavigationReturnType['getItemProps']>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const findInCircularList: <T>(list: T[], from: number, criteria?: (item: T) => boolean, step?: number) => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-hooks-keyboard-navigation",
3
- "version": "3.34.0",
3
+ "version": "3.35.0-rc.1",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Hooks Keyboaord Navigation",
6
6
  "files": [
@@ -36,16 +36,16 @@
36
36
  "indent": 4
37
37
  },
38
38
  "dependencies": {
39
- "@elliemae/ds-props-helpers": "3.34.0",
40
- "@elliemae/ds-typescript-helpers": "3.34.0",
41
- "@elliemae/ds-utilities": "3.34.0"
39
+ "@elliemae/ds-hooks-on-blur-out": "3.35.0-rc.1",
40
+ "@elliemae/ds-props-helpers": "3.35.0-rc.1",
41
+ "@elliemae/ds-typescript-helpers": "3.35.0-rc.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@elliemae/pui-cli": "~9.0.0-next.31",
45
45
  "@xstyled/system": "3.7.0",
46
46
  "styled-components": "~5.3.9",
47
47
  "styled-system": "~5.1.5",
48
- "@elliemae/ds-monorepo-devops": "3.34.0"
48
+ "@elliemae/ds-monorepo-devops": "3.35.0-rc.1"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "lodash": "^4.17.21",