@elliemae/ds-menu-button 3.46.0-rc.1 → 3.46.0-rc.10

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.
Files changed (127) hide show
  1. package/dist/cjs/config/useMenuButton.js +10 -19
  2. package/dist/cjs/config/useMenuButton.js.map +2 -2
  3. package/dist/cjs/config/useSplitInherithedProps.js +33 -15
  4. package/dist/cjs/config/useSplitInherithedProps.js.map +2 -2
  5. package/dist/cjs/index.js +2 -0
  6. package/dist/cjs/index.js.map +2 -2
  7. package/dist/cjs/parts/DSFlyoutMenu/DSFlyoutMenu.js +36 -12
  8. package/dist/cjs/parts/DSFlyoutMenu/DSFlyoutMenu.js.map +3 -3
  9. package/dist/cjs/parts/DSFlyoutMenu/react-desc-prop-types.js +1 -1
  10. package/dist/cjs/parts/DSFlyoutMenu/react-desc-prop-types.js.map +3 -3
  11. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js +86 -0
  12. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js.map +7 -0
  13. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useFocusTracker.js +40 -30
  14. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useFocusTracker.js.map +2 -2
  15. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js +2 -0
  16. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js.map +2 -2
  17. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js +77 -33
  18. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js.map +2 -2
  19. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js +3 -0
  20. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js.map +2 -2
  21. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js +14 -14
  22. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js.map +1 -1
  23. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js +2 -2
  24. package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js.map +1 -1
  25. package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableMenuItem.js +13 -9
  26. package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableMenuItem.js.map +2 -2
  27. package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js +13 -9
  28. package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js.map +2 -2
  29. package/dist/cjs/parts/DSMenuItemRendererFactory/DSMenuItemRendererFactory.js +10 -9
  30. package/dist/cjs/parts/DSMenuItemRendererFactory/DSMenuItemRendererFactory.js.map +2 -2
  31. package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js +15 -11
  32. package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js.map +2 -2
  33. package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js +18 -12
  34. package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js.map +2 -2
  35. package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js +15 -11
  36. package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js.map +2 -2
  37. package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js +15 -11
  38. package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js.map +2 -2
  39. package/dist/cjs/parts/DSMenuItemRendererFactory/SkeletonMenuItem.js +112 -0
  40. package/dist/cjs/parts/DSMenuItemRendererFactory/SkeletonMenuItem.js.map +7 -0
  41. package/dist/cjs/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js +13 -9
  42. package/dist/cjs/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js.map +2 -2
  43. package/dist/cjs/parts/DSMenuItemRendererFactory/config/useMenuItemRendererFactory.js +1 -1
  44. package/dist/cjs/parts/DSMenuItemRendererFactory/config/useMenuItemRendererFactory.js.map +2 -2
  45. package/dist/cjs/parts/DSMenuItemRendererFactory/focusNodeRacingConditionSolved.js +43 -0
  46. package/dist/cjs/parts/DSMenuItemRendererFactory/focusNodeRacingConditionSolved.js.map +7 -0
  47. package/dist/cjs/parts/DSMenuItemRendererFactory/react-desc-prop-types.js.map +2 -2
  48. package/dist/cjs/parts/DSOpinionatedButton/react-desc-prop-types.js.map +2 -2
  49. package/dist/cjs/react-desc-prop-types.js +16 -15
  50. package/dist/cjs/react-desc-prop-types.js.map +2 -2
  51. package/dist/cjs/utils/nodesTypeguardsAndGetters.js +18 -56
  52. package/dist/cjs/utils/nodesTypeguardsAndGetters.js.map +2 -2
  53. package/dist/cjs/utils/useOptionsArrayToDsTree.js +10 -3
  54. package/dist/cjs/utils/useOptionsArrayToDsTree.js.map +2 -2
  55. package/dist/esm/config/useMenuButton.js +11 -20
  56. package/dist/esm/config/useMenuButton.js.map +2 -2
  57. package/dist/esm/config/useSplitInherithedProps.js +33 -15
  58. package/dist/esm/config/useSplitInherithedProps.js.map +2 -2
  59. package/dist/esm/index.js +2 -0
  60. package/dist/esm/index.js.map +2 -2
  61. package/dist/esm/parts/DSFlyoutMenu/DSFlyoutMenu.js +34 -10
  62. package/dist/esm/parts/DSFlyoutMenu/DSFlyoutMenu.js.map +2 -2
  63. package/dist/esm/parts/DSFlyoutMenu/react-desc-prop-types.js +1 -4
  64. package/dist/esm/parts/DSFlyoutMenu/react-desc-prop-types.js.map +2 -2
  65. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js +56 -0
  66. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js.map +7 -0
  67. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useFocusTracker.js +40 -30
  68. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useFocusTracker.js.map +2 -2
  69. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js +2 -0
  70. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js.map +2 -2
  71. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js +77 -33
  72. package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js.map +2 -2
  73. package/dist/esm/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js +3 -0
  74. package/dist/esm/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js.map +2 -2
  75. package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js +14 -14
  76. package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js.map +1 -1
  77. package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js +2 -2
  78. package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js.map +1 -1
  79. package/dist/esm/parts/DSMenuItemRendererFactory/ActivableMenuItem.js +13 -9
  80. package/dist/esm/parts/DSMenuItemRendererFactory/ActivableMenuItem.js.map +2 -2
  81. package/dist/esm/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js +13 -9
  82. package/dist/esm/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js.map +2 -2
  83. package/dist/esm/parts/DSMenuItemRendererFactory/DSMenuItemRendererFactory.js +19 -18
  84. package/dist/esm/parts/DSMenuItemRendererFactory/DSMenuItemRendererFactory.js.map +2 -2
  85. package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js +15 -11
  86. package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js.map +2 -2
  87. package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js +18 -12
  88. package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js.map +2 -2
  89. package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js +15 -11
  90. package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js.map +2 -2
  91. package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js +15 -11
  92. package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js.map +2 -2
  93. package/dist/esm/parts/DSMenuItemRendererFactory/SkeletonMenuItem.js +82 -0
  94. package/dist/esm/parts/DSMenuItemRendererFactory/SkeletonMenuItem.js.map +7 -0
  95. package/dist/esm/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js +13 -9
  96. package/dist/esm/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js.map +2 -2
  97. package/dist/esm/parts/DSMenuItemRendererFactory/config/useMenuItemRendererFactory.js +1 -1
  98. package/dist/esm/parts/DSMenuItemRendererFactory/config/useMenuItemRendererFactory.js.map +2 -2
  99. package/dist/esm/parts/DSMenuItemRendererFactory/focusNodeRacingConditionSolved.js +13 -0
  100. package/dist/esm/parts/DSMenuItemRendererFactory/focusNodeRacingConditionSolved.js.map +7 -0
  101. package/dist/esm/parts/DSMenuItemRendererFactory/react-desc-prop-types.js.map +2 -2
  102. package/dist/esm/parts/DSOpinionatedButton/react-desc-prop-types.js +1 -1
  103. package/dist/esm/parts/DSOpinionatedButton/react-desc-prop-types.js.map +2 -2
  104. package/dist/esm/react-desc-prop-types.js +16 -15
  105. package/dist/esm/react-desc-prop-types.js.map +2 -2
  106. package/dist/esm/utils/nodesTypeguardsAndGetters.js +18 -56
  107. package/dist/esm/utils/nodesTypeguardsAndGetters.js.map +2 -2
  108. package/dist/esm/utils/useOptionsArrayToDsTree.js +10 -3
  109. package/dist/esm/utils/useOptionsArrayToDsTree.js.map +2 -2
  110. package/dist/types/config/useSplitInherithedProps.d.ts +8 -9
  111. package/dist/types/index.d.ts +1 -1
  112. package/dist/types/parts/DSFlyoutMenu/react-desc-prop-types.d.ts +1 -1
  113. package/dist/types/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.d.ts +6 -0
  114. package/dist/types/parts/DSMenuBehaviouralContextProvider/config/useFocusTracker.d.ts +6 -6
  115. package/dist/types/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.d.ts +2 -1
  116. package/dist/types/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.d.ts +1 -0
  117. package/dist/types/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.d.ts +3 -3
  118. package/dist/types/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.d.ts +2 -2
  119. package/dist/types/parts/DSMenuItemRendererFactory/SkeletonMenuItem.d.ts +5 -0
  120. package/dist/types/parts/DSMenuItemRendererFactory/focusNodeRacingConditionSolved.d.ts +2 -0
  121. package/dist/types/parts/DSMenuItemRendererFactory/react-desc-prop-types.d.ts +2 -2
  122. package/dist/types/parts/DSOpinionatedButton/config/useOpinionatedButton.d.ts +1 -1
  123. package/dist/types/parts/DSOpinionatedButton/react-desc-prop-types.d.ts +1 -1
  124. package/dist/types/react-desc-prop-types.d.ts +23 -17
  125. package/dist/types/utils/nodesTypeguardsAndGetters.d.ts +113 -1
  126. package/dist/types/utils/useOptionsArrayToDsTree.d.ts +2 -1
  127. package/package.json +12 -13
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type { DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSMenuButtonT, MenuBehaviouralLayerPropTypes } from '../../react-desc-prop-types.js';\n\nexport declare namespace DSMenuBehaviouralContextProviderT {\n /*\n * everything that is specifically added in this namespace declaration and is not part of the original component\n * is because the original component is an \"opinionated widget\" that offloads a chunk of responsibilities from dev to dimsum\n * as such, the widget generates those interfaces,\n * but the app developer is not supposed to know about them\n * untill they go atomic composition (this namespace)\n */\n export interface RequiredProps extends DSMenuButtonT.MenuBehaviouralLayerRequiredProps {\n buttonDOMNodeRef: React.MutableRefObject<HTMLElement | null>;\n optionsTree: DSMenuButtonT.MenuNode;\n children: TypescriptHelpersT.ReactChildrenComplete;\n focusableNodes: DSMenuButtonT.PseudoFocusableMenuNodes[];\n }\n\n export interface DefaultProps extends DSMenuButtonT.MenuBehaviouralLayerDefaultProps {}\n\n export interface OptionalProps extends DSMenuButtonT.MenuBehaviouralLayerOptionalProps {}\n export interface Props extends Partial<DefaultProps>, OptionalProps, RequiredProps {}\n\n export interface InternalProps extends DefaultProps, OptionalProps, RequiredProps {}\n}\n\nexport const defaultProps: DSMenuBehaviouralContextProviderT.DefaultProps = {};\n\nexport const DSMenuBehaviouralContextProviderPropTypes: DSPropTypesSchema<DSMenuBehaviouralContextProviderT.Props> = {\n buttonDOMNodeRef: PropTypes.object.description(\n 'The DOM node of the button as a mutable Refs (required for performance reasons)',\n ).isRequired,\n children: PropTypes.node.description('the menu trigger and the menu components').isRequired,\n optionsTree: PropTypes.object.description('DSTree node to build branches from').isRequired,\n focusableNodes: PropTypes.arrayOf(PropTypes.object).description(\n 'an array of nodes that can receive pseudoFocus, obtainable via ds-tree flatten + filter(isFocusable)',\n ).isRequired,\n ...MenuBehaviouralLayerPropTypes,\n};\n\nexport const DSMenuBehaviouralContextProviderPropTypesSchema =\n DSMenuBehaviouralContextProviderPropTypes as unknown as ValidationMap<DSMenuBehaviouralContextProviderT.Props>;\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACEvB,SAAS,iBAAiB;AAE1B,SAA6B,qCAAqC;AAyB3D,MAAM,eAA+D,CAAC;AAEtE,MAAM,4CAAwG;AAAA,EACnH,kBAAkB,UAAU,OAAO;AAAA,IACjC;AAAA,EACF,EAAE;AAAA,EACF,UAAU,UAAU,KAAK,YAAY,0CAA0C,EAAE;AAAA,EACjF,aAAa,UAAU,OAAO,YAAY,oCAAoC,EAAE;AAAA,EAChF,gBAAgB,UAAU,QAAQ,UAAU,MAAM,EAAE;AAAA,IAClD;AAAA,EACF,EAAE;AAAA,EACF,GAAG;AACL;AAEO,MAAM,kDACX;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type { DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type DSMenuButtonT, MenuBehaviouralLayerPropTypes } from '../../react-desc-prop-types.js';\n\nexport declare namespace DSMenuBehaviouralContextProviderT {\n /*\n * everything that is specifically added in this namespace declaration and is not part of the original component\n * is because the original component is an \"opinionated widget\" that offloads a chunk of responsibilities from dev to dimsum\n * as such, the widget generates those interfaces,\n * but the app developer is not supposed to know about them\n * untill they go atomic composition (this namespace)\n */\n export interface RequiredProps extends DSMenuButtonT.MenuBehaviouralLayerRequiredProps {\n buttonDOMNodeRef: React.MutableRefObject<HTMLElement | null>;\n optionsTree: DSMenuButtonT.MenuNode;\n children: TypescriptHelpersT.ReactChildrenComplete;\n focusableNodes: DSMenuButtonT.PseudoFocusableMenuNodes[];\n }\n\n export interface DefaultProps extends DSMenuButtonT.MenuBehaviouralLayerDefaultProps {}\n\n export interface OptionalProps extends DSMenuButtonT.MenuBehaviouralLayerOptionalProps {\n selectedNodes?: DSMenuButtonT.SelectionableMenuNodes[];\n }\n export interface Props extends Partial<DefaultProps>, OptionalProps, RequiredProps {}\n\n export interface InternalProps extends DefaultProps, OptionalProps, RequiredProps {}\n}\n\nexport const defaultProps: DSMenuBehaviouralContextProviderT.DefaultProps = {};\n\nexport const DSMenuBehaviouralContextProviderPropTypes: DSPropTypesSchema<DSMenuBehaviouralContextProviderT.Props> = {\n buttonDOMNodeRef: PropTypes.object.description(\n 'The DOM node of the button as a mutable Refs (required for performance reasons)',\n ).isRequired,\n children: PropTypes.node.description('the menu trigger and the menu components').isRequired,\n optionsTree: PropTypes.object.description('DSTree node to build branches from').isRequired,\n focusableNodes: PropTypes.arrayOf(PropTypes.object).description(\n 'an array of nodes that can receive pseudoFocus, obtainable via ds-tree flatten + filter(isFocusable)',\n ).isRequired,\n selectedNodes: PropTypes.arrayOf(PropTypes.object).description(\n 'an array of tree-nodes that have to be marked as selected in the GUI, required if the menu has selectionable items',\n ),\n ...MenuBehaviouralLayerPropTypes,\n};\n\nexport const DSMenuBehaviouralContextProviderPropTypesSchema =\n DSMenuBehaviouralContextProviderPropTypes as unknown as ValidationMap<DSMenuBehaviouralContextProviderT.Props>;\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACEvB,SAAS,iBAAiB;AAE1B,SAA6B,qCAAqC;AA2B3D,MAAM,eAA+D,CAAC;AAEtE,MAAM,4CAAwG;AAAA,EACnH,kBAAkB,UAAU,OAAO;AAAA,IACjC;AAAA,EACF,EAAE;AAAA,EACF,UAAU,UAAU,KAAK,YAAY,0CAA0C,EAAE;AAAA,EACjF,aAAa,UAAU,OAAO,YAAY,oCAAoC,EAAE;AAAA,EAChF,gBAAgB,UAAU,QAAQ,UAAU,MAAM,EAAE;AAAA,IAClD;AAAA,EACF,EAAE;AAAA,EACF,eAAe,UAAU,QAAQ,UAAU,MAAM,EAAE;AAAA,IACjD;AAAA,EACF;AAAA,EACA,GAAG;AACL;AAEO,MAAM,kDACX;",
6
6
  "names": []
7
7
  }
@@ -8,13 +8,13 @@ import { UNEXPECTED_INTERNAL_ERRORS } from "../constants/Errors.js";
8
8
  import { getChildrenByCriterias } from "./nodeGettersByCriterias.js";
9
9
  const getIsMultipleSelectNodeWithSubmenuSelected = ({
10
10
  itemNode,
11
- selectedItems
11
+ selectedNodes
12
12
  }) => {
13
- if (selectedItems.length === 0) return false;
13
+ if (selectedNodes.length === 0) return false;
14
14
  const { multiSelectionableChilrens } = getChildrenByCriterias(itemNode);
15
15
  const selectedMultiChildren = [];
16
16
  let isSelectedItself = false;
17
- selectedItems.forEach((itemMarkesAsSelected) => {
17
+ selectedNodes.forEach((itemMarkesAsSelected) => {
18
18
  if (itemMarkesAsSelected.dsId === itemNode.dsId) isSelectedItself = true;
19
19
  if (isMultipleSelectNode(itemMarkesAsSelected)) {
20
20
  if (multiSelectionableChilrens.some((child) => child.dsId === itemMarkesAsSelected.dsId))
@@ -27,9 +27,9 @@ const getIsMultipleSelectNodeWithSubmenuSelected = ({
27
27
  if (selectedMultiChildren.length !== multiSelectionableChilrens.length) return "mixed";
28
28
  return true;
29
29
  };
30
- const getNewSelectionForMultipleSelectOnlyNode = (selectedItems, selectedItem) => {
31
- const isDeselectingItem = selectedItems.find((item) => item.dsId === selectedItem.dsId) ?? false;
32
- let newSelection = isDeselectingItem ? selectedItems.filter(({ dsId }) => dsId !== selectedItem.dsId) : [...selectedItems, selectedItem];
30
+ const getNewSelectionForMultipleSelectOnlyNode = (selectedNodes, selectedItem) => {
31
+ const isDeselectingItem = selectedNodes.find((item) => item.dsId === selectedItem.dsId) ?? false;
32
+ let newSelection = isDeselectingItem ? selectedNodes.filter(({ dsId }) => dsId !== selectedItem.dsId) : [...selectedNodes, selectedItem];
33
33
  const notSelectedMultiParents = [];
34
34
  const selectedMultiParents = [];
35
35
  const multiParentsToDeselect = [];
@@ -42,7 +42,7 @@ const getNewSelectionForMultipleSelectOnlyNode = (selectedItems, selectedItem) =
42
42
  selectedMultiParents.push(typedParent);
43
43
  const isParentSelected = getIsMultipleSelectNodeWithSubmenuSelected({
44
44
  itemNode: typedParent,
45
- selectedItems: newSelection
45
+ selectedNodes: newSelection
46
46
  });
47
47
  if (isParentSelected === false) multiParentsToDeselect.push(typedParent);
48
48
  } else notSelectedMultiParents.push(typedParent);
@@ -56,13 +56,13 @@ const getNewSelectionForMultipleSelectOnlyNode = (selectedItems, selectedItem) =
56
56
  }
57
57
  return newSelection;
58
58
  };
59
- const getNewSelectionForMultipleSelectionWithSubmenuNode = (selectedItems, selectedItem) => {
59
+ const getNewSelectionForMultipleSelectionWithSubmenuNode = (selectedNodes, selectedItem) => {
60
60
  const currentStatus = getIsMultipleSelectNodeWithSubmenuSelected({
61
61
  itemNode: selectedItem,
62
- selectedItems
62
+ selectedNodes
63
63
  });
64
64
  const isDeselectingItem = currentStatus === true;
65
- let newSelection = isDeselectingItem ? selectedItems.filter(({ dsId }) => dsId !== selectedItem.dsId) : [...selectedItems, selectedItem];
65
+ let newSelection = isDeselectingItem ? selectedNodes.filter(({ dsId }) => dsId !== selectedItem.dsId) : [...selectedNodes, selectedItem];
66
66
  selectedItem.walk((child) => {
67
67
  const typedChild = child;
68
68
  if (isDeselectingItem) {
@@ -84,7 +84,7 @@ const getNewSelectionForMultipleSelectionWithSubmenuNode = (selectedItems, selec
84
84
  selectedMultiParents.push(typedParent);
85
85
  const isParentSelected = getIsMultipleSelectNodeWithSubmenuSelected({
86
86
  itemNode: typedParent,
87
- selectedItems: newSelection
87
+ selectedNodes: newSelection
88
88
  });
89
89
  if (isParentSelected === false) multiParentsToDeselect.push(typedParent);
90
90
  } else notSelectedMultiParents.push(typedParent);
@@ -98,11 +98,11 @@ const getNewSelectionForMultipleSelectionWithSubmenuNode = (selectedItems, selec
98
98
  }
99
99
  return newSelection;
100
100
  };
101
- const getNewSelectionMultipleSelect = (selectedItems, selectedItem) => {
101
+ const getNewSelectionMultipleSelect = (selectedNodes, selectedItem) => {
102
102
  if (isMultipleSelectOnlyNode(selectedItem))
103
- return getNewSelectionForMultipleSelectOnlyNode(selectedItems, selectedItem);
103
+ return getNewSelectionForMultipleSelectOnlyNode(selectedNodes, selectedItem);
104
104
  if (isMultipleSelectWithSubmenuNode(selectedItem))
105
- return getNewSelectionForMultipleSelectionWithSubmenuNode(selectedItems, selectedItem);
105
+ return getNewSelectionForMultipleSelectionWithSubmenuNode(selectedNodes, selectedItem);
106
106
  console.log(selectedItem);
107
107
  throw UNEXPECTED_INTERNAL_ERRORS.CALCULATING_MULTI_SELECTION_OF_NON_MULTI_SELECTION_NODE;
108
108
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../scripts/build/transpile/react-shim.js", "../../../../../src/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable no-console */\n/* eslint-disable max-statements */\nimport { type DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport {\n isMultipleSelectNode,\n isMultipleSelectOnlyNode,\n isMultipleSelectWithSubmenuNode,\n} from '../../../utils/nodesTypeguardsAndGetters.js';\nimport { UNEXPECTED_INTERNAL_ERRORS } from '../constants/Errors.js';\nimport { getChildrenByCriterias } from './nodeGettersByCriterias.js';\n\nexport const getIsMultipleSelectNodeWithSubmenuSelected = ({\n itemNode,\n selectedItems,\n}: {\n itemNode: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem;\n selectedItems: DSMenuButtonT.SelectionableMenuNodes[];\n}) => {\n // no selected items, nothing is selected <- this is not selected neither, nothing else to check\n if (selectedItems.length === 0) return false;\n\n const { multiSelectionableChilrens } = getChildrenByCriterias(itemNode);\n const selectedMultiChildren: DSMenuButtonT.MultipleSelectionableMenuNodes[] = [];\n let isSelectedItself = false;\n selectedItems.forEach((itemMarkesAsSelected) => {\n if (itemMarkesAsSelected.dsId === itemNode.dsId) isSelectedItself = true;\n if (isMultipleSelectNode(itemMarkesAsSelected)) {\n // we reduce scenarios of O(n*m), if the node is not a multi-selectable node, we don't iterate the multiSelectionable childrens\n if (multiSelectionableChilrens.some((child) => child.dsId === itemMarkesAsSelected.dsId))\n selectedMultiChildren.push(itemMarkesAsSelected);\n }\n });\n // console.group(`${itemNode.dsId} > getting \"selected\" (true/false/mixed) status`);\n // console.log({\n // itemNode: itemNode.dsId,\n // selectedItems: selectedItems.map((itm) => itm.dsId),\n // multiSelectionableChilrens: multiSelectionableChilrens.map((itm) => itm.dsId),\n // selectedMultiChildren: selectedMultiChildren.map((itm) => itm.dsId),\n // isSelectedItself,\n // });\n // console.groupEnd();\n // if it's not marked as selected, it's not selected.\n if (!isSelectedItself) return false;\n // if the node doesn't even have multi children, to calculate 'mixed' status upon (it may still have radios)\n // (and is selected itself), it's selected\n if (multiSelectionableChilrens.length === 0) return true;\n\n // multi children exist and the node is selected\n // it can be\n // true if all the multi children are selected\n // mixed if not all the multi children are selected\n // false if all the multi children are not selected\n\n // multi children exist but none are selected at all?\n if (selectedMultiChildren.length === 0) return false;\n // any multi children is not selected?\n if (selectedMultiChildren.length !== multiSelectionableChilrens.length) return 'mixed';\n // there is a multi selection, and everything is selected\n return true;\n};\n\nconst getNewSelectionForMultipleSelectOnlyNode = (\n selectedItems: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MenuNodeMultipleSelectItem,\n) => {\n const isDeselectingItem = selectedItems.find((item) => item.dsId === selectedItem.dsId) ?? false;\n\n let newSelection = isDeselectingItem\n ? selectedItems.filter(({ dsId }) => dsId !== selectedItem.dsId)\n : [...selectedItems, selectedItem];\n const notSelectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const selectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const multiParentsToDeselect: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n\n // we walk the parents and track info about them\n selectedItem.walkParents((parent) => {\n // discard ourself, only interested in the parents\n if (parent.dsId === selectedItem.dsId) return true;\n // DSTree typescripst doesn't understand ethereogenic nodes, so we need to typecast\n const typedParent = parent as unknown as DSMenuButtonT.MenuNode;\n if (isMultipleSelectWithSubmenuNode(typedParent)) {\n // before we add the parent, we need to check if it's already in the selection\n const isAlreadySelected = newSelection.find((item) => item.dsId === typedParent.dsId);\n if (isAlreadySelected) {\n selectedMultiParents.push(typedParent);\n // we check if the parent is mixed or should be false (so we can deselect it)\n const isParentSelected = getIsMultipleSelectNodeWithSubmenuSelected({\n itemNode: typedParent,\n selectedItems: newSelection,\n });\n if (isParentSelected === false) multiParentsToDeselect.push(typedParent);\n } else notSelectedMultiParents.push(typedParent);\n }\n return true;\n });\n\n if (isDeselectingItem) {\n // if we are deselecting:\n // we need to remove the parent nodes that are not selected anymore\n // (they transition from mixed to not selected because of this item deselection)\n newSelection = newSelection.filter(({ dsId }) => !multiParentsToDeselect.some((parent) => parent.dsId === dsId));\n } else {\n // if we are selecting:\n // we need to add the parent nodes that are not selected yet\n // (they transition from not selected to mixed because of this item selection)\n newSelection.push(...notSelectedMultiParents);\n }\n\n return newSelection;\n};\n\nconst getNewSelectionForMultipleSelectionWithSubmenuNode = (\n selectedItems: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem,\n) => {\n // when selecting/deselecting a node with a submenu, we need to consider the hierarchy of the selection starting from here\n // both upwards and downwards\n // - upwards\n // we have to check if any multi-selectable-with-submenu parent transition from not selected to mixed || mixed to selected\n // - downwards\n // - Deselect\n // we have to deselect all the selectable children (regarless of the depth of the children and kind of selection)\n // - Select\n // we have to select all the multi-selectable children that are not selected yet (both with and without submenu)\n\n // console.group(`${selectedItem.dsId} > getting new selection`);\n const currentStatus = getIsMultipleSelectNodeWithSubmenuSelected({\n itemNode: selectedItem,\n selectedItems,\n });\n const isDeselectingItem = currentStatus === true;\n\n let newSelection = isDeselectingItem\n ? selectedItems.filter(({ dsId }) => dsId !== selectedItem.dsId)\n : [...selectedItems, selectedItem];\n\n // console.log(\n // 'starting newSelection',\n // newSelection.map(({ dsId }) => dsId),\n // );\n\n // we walk the children first and update the new selection accordingly\n // (it's important we first traverse the children, so we can update the selection of the parents accordingly to the new selection)\n selectedItem.walk((child) => {\n // DSTree typescripst doesn't understand ethereogenic nodes, so we need to typecast\n const typedChild = child as unknown as DSMenuButtonT.SelectionableMenuNodes;\n if (isDeselectingItem) {\n // if we are deselecting, we need to remove the children from the selection\n newSelection = newSelection.filter(({ dsId }) => dsId !== typedChild.dsId);\n } else if (isMultipleSelectNode(typedChild) && !newSelection.find((item) => item.dsId === typedChild.dsId)) {\n // if we are selecting, we need to add all the multi children to the selection (if they are not already there)\n // (we don't do anything with single-selectable nodes, they are not affected by this operation by definition)\n newSelection.push(typedChild);\n }\n return true;\n });\n\n // console.log(\n // 'newSelection after applying logic to children, pre parents traversal',\n // newSelection.map(({ dsId }) => dsId),\n // );\n\n // now newSelection has the correct information about the node itself and its children\n // we need to update the parents accordingly\n\n const notSelectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const selectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const multiParentsToDeselect: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n // we walk the parents and track info about them\n selectedItem.walkParents((parent) => {\n // discard ourself, only interested in the parents\n if (parent.dsId === selectedItem.dsId) return true;\n // DSTree typescripst doesn't understand ethereogenic nodes, so we need to typecast\n const typedParent = parent as unknown as DSMenuButtonT.MenuNode;\n if (isMultipleSelectWithSubmenuNode(typedParent)) {\n // before we add the parent, we need to check if it's already in the selection\n const isAlreadySelected = newSelection.find((item) => item.dsId === typedParent.dsId);\n if (isAlreadySelected) {\n selectedMultiParents.push(typedParent);\n // we check if the parent is mixed or should be false (so we can deselect it)\n const isParentSelected = getIsMultipleSelectNodeWithSubmenuSelected({\n itemNode: typedParent,\n selectedItems: newSelection,\n });\n if (isParentSelected === false) multiParentsToDeselect.push(typedParent);\n } else notSelectedMultiParents.push(typedParent);\n }\n return true;\n });\n\n // console.log(\n // 'parents info > notSelectedMultiParents',\n // notSelectedMultiParents.map(({ dsId }) => dsId),\n // );\n // console.log(\n // 'parents info > selectedMultiParents',\n // selectedMultiParents.map(({ dsId }) => dsId),\n // );\n // console.log(\n // 'parents info > multiParentsToDeselect',\n // multiParentsToDeselect.map(({ dsId }) => dsId),\n // );\n\n // finally we use the info we gathered to update the selection\n if (isDeselectingItem) {\n // if we are deselecting:\n // we need to remove the parent nodes that are not selected anymore\n // (they transition from mixed to not selected because of this item deselection)\n newSelection = newSelection.filter(({ dsId }) => !multiParentsToDeselect.some((parent) => parent.dsId === dsId));\n } else {\n // if we are selecting:\n // we need to add the parent nodes that are not selected yet\n // (they transition from not selected to mixed because of this item selection)\n newSelection.push(...notSelectedMultiParents);\n }\n\n // console.log(\n // 'final newSelection',\n // newSelection.map(({ dsId }) => dsId),\n // );\n\n // console.groupEnd();\n return newSelection;\n};\n\nexport const getNewSelectionMultipleSelect = (\n selectedItems: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MultipleSelectionableMenuNodes,\n) => {\n // depending on if we are selecting/deselecting an item with/without submenu, we need to handle the selection differently\n if (isMultipleSelectOnlyNode(selectedItem))\n return getNewSelectionForMultipleSelectOnlyNode(selectedItems, selectedItem);\n\n if (isMultipleSelectWithSubmenuNode(selectedItem))\n return getNewSelectionForMultipleSelectionWithSubmenuNode(selectedItems, selectedItem);\n\n // if we reach this point, we are in an invalid state, we should never reach here\n console.log(selectedItem);\n throw UNEXPECTED_INTERNAL_ERRORS.CALCULATING_MULTI_SELECTION_OF_NON_MULTI_SELECTION_NODE;\n};\n"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable no-console */\n/* eslint-disable max-statements */\nimport { type DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport {\n isMultipleSelectNode,\n isMultipleSelectOnlyNode,\n isMultipleSelectWithSubmenuNode,\n} from '../../../utils/nodesTypeguardsAndGetters.js';\nimport { UNEXPECTED_INTERNAL_ERRORS } from '../constants/Errors.js';\nimport { getChildrenByCriterias } from './nodeGettersByCriterias.js';\n\nexport const getIsMultipleSelectNodeWithSubmenuSelected = ({\n itemNode,\n selectedNodes,\n}: {\n itemNode: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem;\n selectedNodes: DSMenuButtonT.SelectionableMenuNodes[];\n}) => {\n // no selected items, nothing is selected <- this is not selected neither, nothing else to check\n if (selectedNodes.length === 0) return false;\n\n const { multiSelectionableChilrens } = getChildrenByCriterias(itemNode);\n const selectedMultiChildren: DSMenuButtonT.MultipleSelectionableMenuNodes[] = [];\n let isSelectedItself = false;\n selectedNodes.forEach((itemMarkesAsSelected) => {\n if (itemMarkesAsSelected.dsId === itemNode.dsId) isSelectedItself = true;\n if (isMultipleSelectNode(itemMarkesAsSelected)) {\n // we reduce scenarios of O(n*m), if the node is not a multi-selectable node, we don't iterate the multiSelectionable childrens\n if (multiSelectionableChilrens.some((child) => child.dsId === itemMarkesAsSelected.dsId))\n selectedMultiChildren.push(itemMarkesAsSelected);\n }\n });\n // console.group(`${itemNode.dsId} > getting \"selected\" (true/false/mixed) status`);\n // console.log({\n // itemNode: itemNode.dsId,\n // selectedNodes: selectedNodes.map((itm) => itm.dsId),\n // multiSelectionableChilrens: multiSelectionableChilrens.map((itm) => itm.dsId),\n // selectedMultiChildren: selectedMultiChildren.map((itm) => itm.dsId),\n // isSelectedItself,\n // });\n // console.groupEnd();\n // if it's not marked as selected, it's not selected.\n if (!isSelectedItself) return false;\n // if the node doesn't even have multi children, to calculate 'mixed' status upon (it may still have radios)\n // (and is selected itself), it's selected\n if (multiSelectionableChilrens.length === 0) return true;\n\n // multi children exist and the node is selected\n // it can be\n // true if all the multi children are selected\n // mixed if not all the multi children are selected\n // false if all the multi children are not selected\n\n // multi children exist but none are selected at all?\n if (selectedMultiChildren.length === 0) return false;\n // any multi children is not selected?\n if (selectedMultiChildren.length !== multiSelectionableChilrens.length) return 'mixed';\n // there is a multi selection, and everything is selected\n return true;\n};\n\nconst getNewSelectionForMultipleSelectOnlyNode = (\n selectedNodes: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MenuNodeMultipleSelectItem,\n) => {\n const isDeselectingItem = selectedNodes.find((item) => item.dsId === selectedItem.dsId) ?? false;\n\n let newSelection = isDeselectingItem\n ? selectedNodes.filter(({ dsId }) => dsId !== selectedItem.dsId)\n : [...selectedNodes, selectedItem];\n const notSelectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const selectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const multiParentsToDeselect: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n\n // we walk the parents and track info about them\n selectedItem.walkParents((parent) => {\n // discard ourself, only interested in the parents\n if (parent.dsId === selectedItem.dsId) return true;\n // DSTree typescripst doesn't understand ethereogenic nodes, so we need to typecast\n const typedParent = parent as unknown as DSMenuButtonT.MenuNode;\n if (isMultipleSelectWithSubmenuNode(typedParent)) {\n // before we add the parent, we need to check if it's already in the selection\n const isAlreadySelected = newSelection.find((item) => item.dsId === typedParent.dsId);\n if (isAlreadySelected) {\n selectedMultiParents.push(typedParent);\n // we check if the parent is mixed or should be false (so we can deselect it)\n const isParentSelected = getIsMultipleSelectNodeWithSubmenuSelected({\n itemNode: typedParent,\n selectedNodes: newSelection,\n });\n if (isParentSelected === false) multiParentsToDeselect.push(typedParent);\n } else notSelectedMultiParents.push(typedParent);\n }\n return true;\n });\n\n if (isDeselectingItem) {\n // if we are deselecting:\n // we need to remove the parent nodes that are not selected anymore\n // (they transition from mixed to not selected because of this item deselection)\n newSelection = newSelection.filter(({ dsId }) => !multiParentsToDeselect.some((parent) => parent.dsId === dsId));\n } else {\n // if we are selecting:\n // we need to add the parent nodes that are not selected yet\n // (they transition from not selected to mixed because of this item selection)\n newSelection.push(...notSelectedMultiParents);\n }\n\n return newSelection;\n};\n\nconst getNewSelectionForMultipleSelectionWithSubmenuNode = (\n selectedNodes: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem,\n) => {\n // when selecting/deselecting a node with a submenu, we need to consider the hierarchy of the selection starting from here\n // both upwards and downwards\n // - upwards\n // we have to check if any multi-selectable-with-submenu parent transition from not selected to mixed || mixed to selected\n // - downwards\n // - Deselect\n // we have to deselect all the selectable children (regarless of the depth of the children and kind of selection)\n // - Select\n // we have to select all the multi-selectable children that are not selected yet (both with and without submenu)\n\n // console.group(`${selectedItem.dsId} > getting new selection`);\n const currentStatus = getIsMultipleSelectNodeWithSubmenuSelected({\n itemNode: selectedItem,\n selectedNodes,\n });\n const isDeselectingItem = currentStatus === true;\n\n let newSelection = isDeselectingItem\n ? selectedNodes.filter(({ dsId }) => dsId !== selectedItem.dsId)\n : [...selectedNodes, selectedItem];\n\n // console.log(\n // 'starting newSelection',\n // newSelection.map(({ dsId }) => dsId),\n // );\n\n // we walk the children first and update the new selection accordingly\n // (it's important we first traverse the children, so we can update the selection of the parents accordingly to the new selection)\n selectedItem.walk((child) => {\n // DSTree typescripst doesn't understand ethereogenic nodes, so we need to typecast\n const typedChild = child as unknown as DSMenuButtonT.SelectionableMenuNodes;\n if (isDeselectingItem) {\n // if we are deselecting, we need to remove the children from the selection\n newSelection = newSelection.filter(({ dsId }) => dsId !== typedChild.dsId);\n } else if (isMultipleSelectNode(typedChild) && !newSelection.find((item) => item.dsId === typedChild.dsId)) {\n // if we are selecting, we need to add all the multi children to the selection (if they are not already there)\n // (we don't do anything with single-selectable nodes, they are not affected by this operation by definition)\n newSelection.push(typedChild);\n }\n return true;\n });\n\n // console.log(\n // 'newSelection after applying logic to children, pre parents traversal',\n // newSelection.map(({ dsId }) => dsId),\n // );\n\n // now newSelection has the correct information about the node itself and its children\n // we need to update the parents accordingly\n\n const notSelectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const selectedMultiParents: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n const multiParentsToDeselect: DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem[] = [];\n // we walk the parents and track info about them\n selectedItem.walkParents((parent) => {\n // discard ourself, only interested in the parents\n if (parent.dsId === selectedItem.dsId) return true;\n // DSTree typescripst doesn't understand ethereogenic nodes, so we need to typecast\n const typedParent = parent as unknown as DSMenuButtonT.MenuNode;\n if (isMultipleSelectWithSubmenuNode(typedParent)) {\n // before we add the parent, we need to check if it's already in the selection\n const isAlreadySelected = newSelection.find((item) => item.dsId === typedParent.dsId);\n if (isAlreadySelected) {\n selectedMultiParents.push(typedParent);\n // we check if the parent is mixed or should be false (so we can deselect it)\n const isParentSelected = getIsMultipleSelectNodeWithSubmenuSelected({\n itemNode: typedParent,\n selectedNodes: newSelection,\n });\n if (isParentSelected === false) multiParentsToDeselect.push(typedParent);\n } else notSelectedMultiParents.push(typedParent);\n }\n return true;\n });\n\n // console.log(\n // 'parents info > notSelectedMultiParents',\n // notSelectedMultiParents.map(({ dsId }) => dsId),\n // );\n // console.log(\n // 'parents info > selectedMultiParents',\n // selectedMultiParents.map(({ dsId }) => dsId),\n // );\n // console.log(\n // 'parents info > multiParentsToDeselect',\n // multiParentsToDeselect.map(({ dsId }) => dsId),\n // );\n\n // finally we use the info we gathered to update the selection\n if (isDeselectingItem) {\n // if we are deselecting:\n // we need to remove the parent nodes that are not selected anymore\n // (they transition from mixed to not selected because of this item deselection)\n newSelection = newSelection.filter(({ dsId }) => !multiParentsToDeselect.some((parent) => parent.dsId === dsId));\n } else {\n // if we are selecting:\n // we need to add the parent nodes that are not selected yet\n // (they transition from not selected to mixed because of this item selection)\n newSelection.push(...notSelectedMultiParents);\n }\n\n // console.log(\n // 'final newSelection',\n // newSelection.map(({ dsId }) => dsId),\n // );\n\n // console.groupEnd();\n return newSelection;\n};\n\nexport const getNewSelectionMultipleSelect = (\n selectedNodes: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MultipleSelectionableMenuNodes,\n) => {\n // depending on if we are selecting/deselecting an item with/without submenu, we need to handle the selection differently\n if (isMultipleSelectOnlyNode(selectedItem))\n return getNewSelectionForMultipleSelectOnlyNode(selectedNodes, selectedItem);\n\n if (isMultipleSelectWithSubmenuNode(selectedItem))\n return getNewSelectionForMultipleSelectionWithSubmenuNode(selectedNodes, selectedItem);\n\n // if we reach this point, we are in an invalid state, we should never reach here\n console.log(selectedItem);\n throw UNEXPECTED_INTERNAL_ERRORS.CALCULATING_MULTI_SELECTION_OF_NON_MULTI_SELECTION_NODE;\n};\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,9 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { getAllSingleSelectIdsFromParent, getParentsByCriterias } from "./nodeGettersByCriterias.js";
3
- const getNewSelectionSingleSelect = (selectedItems, selectedItem) => {
3
+ const getNewSelectionSingleSelect = (selectedNodes, selectedItem) => {
4
4
  const { oldestGroup, selectionableParentsWithSubmenu } = getParentsByCriterias(selectedItem);
5
5
  const itemsIdsToDeselect = getAllSingleSelectIdsFromParent(oldestGroup);
6
- const newSelection = selectedItems.filter((item) => !itemsIdsToDeselect.includes(item.plainItem.dsId));
6
+ const newSelection = selectedNodes.filter((item) => !itemsIdsToDeselect.includes(item.plainItem.dsId));
7
7
  newSelection.push(...selectionableParentsWithSubmenu);
8
8
  newSelection.push(selectedItem);
9
9
  return newSelection;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../scripts/build/transpile/react-shim.js", "../../../../../src/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { type DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport { getAllSingleSelectIdsFromParent, getParentsByCriterias } from './nodeGettersByCriterias.js';\n\n/**\n * When a single-selectable item is selected, all other single-selectable items in the same group are deselected;\n *\n * If the (visual) parent is a single-select-with-submenu,\n * the selection must be propagated because\n * a single-select within a single-select-with-submenu can't be selected without implying the selection of the parent single-select-with-submenu\n *\n * @param selectedItems - the current selected items\n * @param selectedItem - the newly selected item\n * @returns DSMenuButtonT.SelectionableMenuNodes[] - the new selection\n */\nexport const getNewSelectionSingleSelect = (\n selectedItems: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MenuNodeSingleSelectItem | DSMenuButtonT.MenuNodeSingleSelectWithSubmenuItem,\n) => {\n const { oldestGroup, selectionableParentsWithSubmenu } = getParentsByCriterias(selectedItem);\n const itemsIdsToDeselect = getAllSingleSelectIdsFromParent(oldestGroup);\n // we remove all the ids of single selection parents from the previous selection\n const newSelection = selectedItems.filter((item) => !itemsIdsToDeselect.includes(item.plainItem.dsId));\n // we add all the parents submenus to the selection\n newSelection.push(...selectionableParentsWithSubmenu);\n // finally we add the newly selected item\n newSelection.push(selectedItem);\n\n return newSelection;\n};\n"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { type DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport { getAllSingleSelectIdsFromParent, getParentsByCriterias } from './nodeGettersByCriterias.js';\n\n/**\n * When a single-selectable item is selected, all other single-selectable items in the same group are deselected;\n *\n * If the (visual) parent is a single-select-with-submenu,\n * the selection must be propagated because\n * a single-select within a single-select-with-submenu can't be selected without implying the selection of the parent single-select-with-submenu\n *\n * @param selectedNodes - the current selected items\n * @param selectedItem - the newly selected item\n * @returns DSMenuButtonT.SelectionableMenuNodes[] - the new selection\n */\nexport const getNewSelectionSingleSelect = (\n selectedNodes: DSMenuButtonT.SelectionableMenuNodes[],\n selectedItem: DSMenuButtonT.MenuNodeSingleSelectItem | DSMenuButtonT.MenuNodeSingleSelectWithSubmenuItem,\n) => {\n const { oldestGroup, selectionableParentsWithSubmenu } = getParentsByCriterias(selectedItem);\n const itemsIdsToDeselect = getAllSingleSelectIdsFromParent(oldestGroup);\n // we remove all the ids of single selection parents from the previous selection\n const newSelection = selectedNodes.filter((item) => !itemsIdsToDeselect.includes(item.plainItem.dsId));\n // we add all the parents submenus to the selection\n newSelection.push(...selectionableParentsWithSubmenu);\n // finally we add the newly selected item\n newSelection.push(selectedItem);\n\n return newSelection;\n};\n"],
5
5
  "mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,iCAAiC,6BAA6B;AAahE,MAAM,8BAA8B,CACzC,eACA,iBACG;AACH,QAAM,EAAE,aAAa,gCAAgC,IAAI,sBAAsB,YAAY;AAC3F,QAAM,qBAAqB,gCAAgC,WAAW;AAEtE,QAAM,eAAe,cAAc,OAAO,CAAC,SAAS,CAAC,mBAAmB,SAAS,KAAK,UAAU,IAAI,CAAC;AAErG,eAAa,KAAK,GAAG,+BAA+B;AAEpD,eAAa,KAAK,YAAY;AAE9B,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -9,6 +9,7 @@ import {
9
9
  import React2 from "react";
10
10
  import { MenuBehaviouralContextProviderContext } from "../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js";
11
11
  import { MENU_FOCUS_REGIONS } from "../DSMenuBehaviouralContextProvider/constants/index.js";
12
+ import { focusNodeRacingConditionSolved } from "./focusNodeRacingConditionSolved.js";
12
13
  const ActivableMenuItem = ({
13
14
  itemNode
14
15
  }) => {
@@ -19,7 +20,8 @@ const ActivableMenuItem = ({
19
20
  menuItemEventsHandlers: {
20
21
  handleFocusableMenuItemKeyDown,
21
22
  handleFocusableMenuItemClick,
22
- handleFocusableMenuItemOnMouseEnter
23
+ handleFocusableMenuItemOnMouseEnter,
24
+ handleFocusableMenuItemNativeFocusEvent
23
25
  }
24
26
  } = React2.useContext(MenuBehaviouralContextProviderContext);
25
27
  const gridLayout = React2.useMemo(() => {
@@ -32,11 +34,9 @@ const ActivableMenuItem = ({
32
34
  const isFocused = focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId);
33
35
  const handleFocusOnRender = React2.useCallback(
34
36
  (node) => {
35
- setTimeout(() => {
36
- if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {
37
- node.focus();
38
- }
39
- });
37
+ if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {
38
+ focusNodeRacingConditionSolved(node);
39
+ }
40
40
  },
41
41
  // we need to change the callback reference every time the focusRegion changes or else the focus will not be set
42
42
  [dsId, focusRegion]
@@ -44,11 +44,14 @@ const ActivableMenuItem = ({
44
44
  // the logic here actually receives a ref to a HTMLDivElement,
45
45
  // but the component must think this is a HTMLLIElement ref callback
46
46
  );
47
- const handleOnMouseEnter = React2.useCallback(
47
+ const handleOnMouseEnter = React2.useCallback(() => {
48
+ handleFocusableMenuItemOnMouseEnter(itemNode);
49
+ }, [handleFocusableMenuItemOnMouseEnter, itemNode]);
50
+ const handleOnFocus = React2.useCallback(
48
51
  (e) => {
49
- handleFocusableMenuItemOnMouseEnter(itemNode, e);
52
+ handleFocusableMenuItemNativeFocusEvent(itemNode, e);
50
53
  },
51
- [handleFocusableMenuItemOnMouseEnter, itemNode]
54
+ [handleFocusableMenuItemNativeFocusEvent, itemNode]
52
55
  );
53
56
  const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\s/g, "")}`;
54
57
  return /* @__PURE__ */ jsx(
@@ -58,6 +61,7 @@ const ActivableMenuItem = ({
58
61
  onKeyDown: handleFocusableMenuItemKeyDown,
59
62
  onClick: handleFocusableMenuItemClick,
60
63
  onMouseEnter: handleOnMouseEnter,
64
+ onFocus: handleOnFocus,
61
65
  as: "div",
62
66
  id: `${spacelessDsIdForDom}`,
63
67
  role: "menuitem",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/DSMenuItemRendererFactory/ActivableMenuItem.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\nimport {\n StyleMenuItemLabel,\n StyleMenuItemSecondaryLabel,\n StyledContentWrapper,\n StyledGlobalMenuItemWrapper,\n} from '@elliemae/ds-menu-items-commons';\nimport React from 'react';\nimport type { DSMenuButtonT } from '../../react-desc-prop-types.js';\nimport { MenuBehaviouralContextProviderContext } from '../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js';\nimport { MENU_FOCUS_REGIONS } from '../DSMenuBehaviouralContextProvider/constants/index.js';\n\nexport const ActivableMenuItem: React.ComponentType<{ itemNode: DSMenuButtonT.MenuNodeActivableItem }> = ({\n itemNode,\n}) => {\n const { dsId, plainItem } = itemNode;\n const { label, secondaryLabel, leftDecoration: LeftDecComponent, minWidth, disabled } = plainItem;\n\n const {\n focusRegion,\n menuItemEventsHandlers: {\n handleFocusableMenuItemKeyDown,\n handleFocusableMenuItemClick,\n handleFocusableMenuItemOnMouseEnter,\n },\n } = React.useContext(MenuBehaviouralContextProviderContext);\n\n const gridLayout = React.useMemo(() => {\n const cols = LeftDecComponent ? ['min-content', 'auto'] : ['auto'];\n if (secondaryLabel) cols.push('auto');\n return cols;\n }, [LeftDecComponent, secondaryLabel]);\n\n const focusedRegionPerformanceHelper = React.useRef(focusRegion);\n focusedRegionPerformanceHelper.current = focusRegion;\n\n const isFocused = focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId);\n\n const handleFocusOnRender = React.useCallback(\n (node: HTMLDivElement) => {\n setTimeout(() => {\n if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {\n node.focus();\n }\n });\n },\n // we need to change the callback reference every time the focusRegion changes or else the focus will not be set\n [dsId, focusRegion],\n // we are using the \"as='div'\", typescript is not able to infer the correct type\n // the logic here actually receives a ref to a HTMLDivElement,\n // but the component must think this is a HTMLLIElement ref callback\n ) as unknown as React.RefCallback<HTMLLIElement>;\n const handleOnMouseEnter = React.useCallback<React.MouseEventHandler<HTMLDivElement>>(\n (e) => {\n handleFocusableMenuItemOnMouseEnter(itemNode, e);\n },\n [handleFocusableMenuItemOnMouseEnter, itemNode],\n );\n\n const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\\s/g, '')}`;\n\n return (\n <StyledGlobalMenuItemWrapper\n innerRef={handleFocusOnRender}\n onKeyDown={handleFocusableMenuItemKeyDown}\n onClick={handleFocusableMenuItemClick}\n onMouseEnter={handleOnMouseEnter}\n as=\"div\"\n id={`${spacelessDsIdForDom}`}\n role=\"menuitem\"\n tabIndex={isFocused ? 0 : -1}\n aria-disabled={disabled}\n applyAriaDisabled={disabled}\n >\n <StyledContentWrapper\n cols={gridLayout}\n minHeight=\"16px\"\n gutter=\"xxs\"\n alignItems=\"center\"\n minWidth={minWidth ?? undefined}\n >\n {LeftDecComponent ? <LeftDecComponent /> : null}\n <StyleMenuItemLabel>{label}</StyleMenuItemLabel>\n {secondaryLabel !== undefined && (\n <StyleMenuItemSecondaryLabel disabled={disabled}>{secondaryLabel}</StyleMenuItemSecondaryLabel>\n )}\n </StyledContentWrapper>\n </StyledGlobalMenuItemWrapper>\n );\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;AC0EjB,SAOsB,KAPtB;AAzEN;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,OAAOA,YAAW;AAElB,SAAS,6CAA6C;AACtD,SAAS,0BAA0B;AAE5B,MAAM,oBAA4F,CAAC;AAAA,EACxG;AACF,MAAM;AACJ,QAAM,EAAE,MAAM,UAAU,IAAI;AAC5B,QAAM,EAAE,OAAO,gBAAgB,gBAAgB,kBAAkB,UAAU,SAAS,IAAI;AAExF,QAAM;AAAA,IACJ;AAAA,IACA,wBAAwB;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,IAAIA,OAAM,WAAW,qCAAqC;AAE1D,QAAM,aAAaA,OAAM,QAAQ,MAAM;AACrC,UAAM,OAAO,mBAAmB,CAAC,eAAe,MAAM,IAAI,CAAC,MAAM;AACjE,QAAI,eAAgB,MAAK,KAAK,MAAM;AACpC,WAAO;AAAA,EACT,GAAG,CAAC,kBAAkB,cAAc,CAAC;AAErC,QAAM,iCAAiCA,OAAM,OAAO,WAAW;AAC/D,iCAA+B,UAAU;AAEzC,QAAM,YAAY,gBAAgB,mBAAmB,aAAa,IAAI;AAEtE,QAAM,sBAAsBA,OAAM;AAAA,IAChC,CAAC,SAAyB;AACxB,iBAAW,MAAM;AACf,YAAI,QAAQ,gBAAgB,mBAAmB,aAAa,IAAI,GAAG;AACjE,eAAK,MAAM;AAAA,QACb;AAAA,MACF,CAAC;AAAA,IACH;AAAA;AAAA,IAEA,CAAC,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA,EAIpB;AACA,QAAM,qBAAqBA,OAAM;AAAA,IAC/B,CAAC,MAAM;AACL,0CAAoC,UAAU,CAAC;AAAA,IACjD;AAAA,IACA,CAAC,qCAAqC,QAAQ;AAAA,EAChD;AAEA,QAAM,sBAAsB,gBAAgB,GAAG,IAAI,GAAG,QAAQ,OAAO,EAAE,CAAC;AAExE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,cAAc;AAAA,MACd,IAAG;AAAA,MACH,IAAI,GAAG,mBAAmB;AAAA,MAC1B,MAAK;AAAA,MACL,UAAU,YAAY,IAAI;AAAA,MAC1B,iBAAe;AAAA,MACf,mBAAmB;AAAA,MAEnB;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,WAAU;AAAA,UACV,QAAO;AAAA,UACP,YAAW;AAAA,UACX,UAAU,YAAY;AAAA,UAErB;AAAA,+BAAmB,oBAAC,oBAAiB,IAAK;AAAA,YAC3C,oBAAC,sBAAoB,iBAAM;AAAA,YAC1B,mBAAmB,UAClB,oBAAC,+BAA4B,UAAqB,0BAAe;AAAA;AAAA;AAAA,MAErE;AAAA;AAAA,EACF;AAEJ;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\nimport {\n StyleMenuItemLabel,\n StyleMenuItemSecondaryLabel,\n StyledContentWrapper,\n StyledGlobalMenuItemWrapper,\n} from '@elliemae/ds-menu-items-commons';\nimport React from 'react';\nimport type { DSMenuButtonT } from '../../react-desc-prop-types.js';\nimport { MenuBehaviouralContextProviderContext } from '../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js';\nimport { MENU_FOCUS_REGIONS } from '../DSMenuBehaviouralContextProvider/constants/index.js';\nimport { focusNodeRacingConditionSolved } from './focusNodeRacingConditionSolved.js';\n\nexport const ActivableMenuItem: React.ComponentType<{ itemNode: DSMenuButtonT.MenuNodeActivableItem }> = ({\n itemNode,\n}) => {\n const { dsId, plainItem } = itemNode;\n const { label, secondaryLabel, leftDecoration: LeftDecComponent, minWidth, disabled } = plainItem;\n\n const {\n focusRegion,\n menuItemEventsHandlers: {\n handleFocusableMenuItemKeyDown,\n handleFocusableMenuItemClick,\n handleFocusableMenuItemOnMouseEnter,\n handleFocusableMenuItemNativeFocusEvent,\n },\n } = React.useContext(MenuBehaviouralContextProviderContext);\n\n const gridLayout = React.useMemo(() => {\n const cols = LeftDecComponent ? ['min-content', 'auto'] : ['auto'];\n if (secondaryLabel) cols.push('auto');\n return cols;\n }, [LeftDecComponent, secondaryLabel]);\n\n const focusedRegionPerformanceHelper = React.useRef(focusRegion);\n focusedRegionPerformanceHelper.current = focusRegion;\n\n const isFocused = focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId);\n\n const handleFocusOnRender = React.useCallback(\n (node: HTMLDivElement) => {\n if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {\n focusNodeRacingConditionSolved(node);\n }\n },\n // we need to change the callback reference every time the focusRegion changes or else the focus will not be set\n [dsId, focusRegion],\n // we are using the \"as='div'\", typescript is not able to infer the correct type\n // the logic here actually receives a ref to a HTMLDivElement,\n // but the component must think this is a HTMLLIElement ref callback\n ) as unknown as React.RefCallback<HTMLLIElement>;\n const handleOnMouseEnter = React.useCallback<React.MouseEventHandler<HTMLDivElement>>(() => {\n handleFocusableMenuItemOnMouseEnter(itemNode);\n }, [handleFocusableMenuItemOnMouseEnter, itemNode]);\n const handleOnFocus = React.useCallback<React.FocusEventHandler<HTMLDivElement>>(\n (e) => {\n handleFocusableMenuItemNativeFocusEvent(itemNode, e);\n },\n [handleFocusableMenuItemNativeFocusEvent, itemNode],\n );\n\n const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\\s/g, '')}`;\n\n return (\n <StyledGlobalMenuItemWrapper\n innerRef={handleFocusOnRender}\n onKeyDown={handleFocusableMenuItemKeyDown}\n onClick={handleFocusableMenuItemClick}\n onMouseEnter={handleOnMouseEnter}\n onFocus={handleOnFocus}\n as=\"div\"\n id={`${spacelessDsIdForDom}`}\n role=\"menuitem\"\n tabIndex={isFocused ? 0 : -1}\n aria-disabled={disabled}\n applyAriaDisabled={disabled}\n >\n <StyledContentWrapper\n cols={gridLayout}\n minHeight=\"16px\"\n gutter=\"xxs\"\n alignItems=\"center\"\n minWidth={minWidth ?? undefined}\n >\n {LeftDecComponent ? <LeftDecComponent /> : null}\n <StyleMenuItemLabel>{label}</StyleMenuItemLabel>\n {secondaryLabel !== undefined && (\n <StyleMenuItemSecondaryLabel disabled={disabled}>{secondaryLabel}</StyleMenuItemSecondaryLabel>\n )}\n </StyledContentWrapper>\n </StyledGlobalMenuItemWrapper>\n );\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AC8EjB,SAOsB,KAPtB;AA7EN;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,OAAOA,YAAW;AAElB,SAAS,6CAA6C;AACtD,SAAS,0BAA0B;AACnC,SAAS,sCAAsC;AAExC,MAAM,oBAA4F,CAAC;AAAA,EACxG;AACF,MAAM;AACJ,QAAM,EAAE,MAAM,UAAU,IAAI;AAC5B,QAAM,EAAE,OAAO,gBAAgB,gBAAgB,kBAAkB,UAAU,SAAS,IAAI;AAExF,QAAM;AAAA,IACJ;AAAA,IACA,wBAAwB;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,IAAIA,OAAM,WAAW,qCAAqC;AAE1D,QAAM,aAAaA,OAAM,QAAQ,MAAM;AACrC,UAAM,OAAO,mBAAmB,CAAC,eAAe,MAAM,IAAI,CAAC,MAAM;AACjE,QAAI,eAAgB,MAAK,KAAK,MAAM;AACpC,WAAO;AAAA,EACT,GAAG,CAAC,kBAAkB,cAAc,CAAC;AAErC,QAAM,iCAAiCA,OAAM,OAAO,WAAW;AAC/D,iCAA+B,UAAU;AAEzC,QAAM,YAAY,gBAAgB,mBAAmB,aAAa,IAAI;AAEtE,QAAM,sBAAsBA,OAAM;AAAA,IAChC,CAAC,SAAyB;AACxB,UAAI,QAAQ,gBAAgB,mBAAmB,aAAa,IAAI,GAAG;AACjE,uCAA+B,IAAI;AAAA,MACrC;AAAA,IACF;AAAA;AAAA,IAEA,CAAC,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA,EAIpB;AACA,QAAM,qBAAqBA,OAAM,YAAqD,MAAM;AAC1F,wCAAoC,QAAQ;AAAA,EAC9C,GAAG,CAAC,qCAAqC,QAAQ,CAAC;AAClD,QAAM,gBAAgBA,OAAM;AAAA,IAC1B,CAAC,MAAM;AACL,8CAAwC,UAAU,CAAC;AAAA,IACrD;AAAA,IACA,CAAC,yCAAyC,QAAQ;AAAA,EACpD;AAEA,QAAM,sBAAsB,gBAAgB,GAAG,IAAI,GAAG,QAAQ,OAAO,EAAE,CAAC;AAExE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV,WAAW;AAAA,MACX,SAAS;AAAA,MACT,cAAc;AAAA,MACd,SAAS;AAAA,MACT,IAAG;AAAA,MACH,IAAI,GAAG,mBAAmB;AAAA,MAC1B,MAAK;AAAA,MACL,UAAU,YAAY,IAAI;AAAA,MAC1B,iBAAe;AAAA,MACf,mBAAmB;AAAA,MAEnB;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,WAAU;AAAA,UACV,QAAO;AAAA,UACP,YAAW;AAAA,UACX,UAAU,YAAY;AAAA,UAErB;AAAA,+BAAmB,oBAAC,oBAAiB,IAAK;AAAA,YAC3C,oBAAC,sBAAoB,iBAAM;AAAA,YAC1B,mBAAmB,UAClB,oBAAC,+BAA4B,UAAqB,0BAAe;AAAA;AAAA;AAAA,MAErE;AAAA;AAAA,EACF;AAEJ;",
6
6
  "names": ["React"]
7
7
  }
@@ -10,6 +10,7 @@ import React2 from "react";
10
10
  import { useFloatingContext } from "@elliemae/ds-floating-context";
11
11
  import { MenuBehaviouralContextProviderContext } from "../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js";
12
12
  import { MENU_FOCUS_REGIONS } from "../DSMenuBehaviouralContextProvider/constants/index.js";
13
+ import { focusNodeRacingConditionSolved } from "./focusNodeRacingConditionSolved.js";
13
14
  const placementOrderPreference = [
14
15
  "right-start",
15
16
  "right-start",
@@ -36,7 +37,8 @@ const ActivableWithSubmenuMenuItem = ({ itemNode, FlyoutMenuCircularDepInject })
36
37
  menuItemEventsHandlers: {
37
38
  handleFocusableMenuItemKeyDown,
38
39
  handleFocusableMenuItemClick,
39
- handleFocusableMenuItemOnMouseEnter
40
+ handleFocusableMenuItemOnMouseEnter,
41
+ handleFocusableMenuItemNativeFocusEvent
40
42
  }
41
43
  } = React2.useContext(MenuBehaviouralContextProviderContext);
42
44
  const gridLayout = React2.useMemo(() => {
@@ -51,11 +53,9 @@ const ActivableWithSubmenuMenuItem = ({ itemNode, FlyoutMenuCircularDepInject })
51
53
  const handleFocusOnRender = React2.useCallback(
52
54
  (node) => {
53
55
  setReference(node);
54
- setTimeout(() => {
55
- if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {
56
- node.focus();
57
- }
58
- });
56
+ if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {
57
+ focusNodeRacingConditionSolved(node);
58
+ }
59
59
  },
60
60
  // we need to change the callback reference every time the focusRegion changes or else the focus will not be set
61
61
  [dsId, focusRegion, setReference]
@@ -63,11 +63,14 @@ const ActivableWithSubmenuMenuItem = ({ itemNode, FlyoutMenuCircularDepInject })
63
63
  // the logic here actually receives a ref to a HTMLDivElement,
64
64
  // but the component must think this is a HTMLLIElement ref callback
65
65
  );
66
- const handleOnMouseEnter = React2.useCallback(
66
+ const handleOnMouseEnter = React2.useCallback(() => {
67
+ handleFocusableMenuItemOnMouseEnter(itemNode);
68
+ }, [handleFocusableMenuItemOnMouseEnter, itemNode]);
69
+ const handleOnFocus = React2.useCallback(
67
70
  (e) => {
68
- handleFocusableMenuItemOnMouseEnter(itemNode, e);
71
+ handleFocusableMenuItemNativeFocusEvent(itemNode, e);
69
72
  },
70
- [handleFocusableMenuItemOnMouseEnter, itemNode]
73
+ [handleFocusableMenuItemNativeFocusEvent, itemNode]
71
74
  );
72
75
  const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\s/g, "")}`;
73
76
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -78,6 +81,7 @@ const ActivableWithSubmenuMenuItem = ({ itemNode, FlyoutMenuCircularDepInject })
78
81
  onKeyDown: handleFocusableMenuItemKeyDown,
79
82
  onClick: handleFocusableMenuItemClick,
80
83
  onMouseEnter: handleOnMouseEnter,
84
+ onFocus: handleOnFocus,
81
85
  as: "div",
82
86
  id: `${spacelessDsIdForDom}`,
83
87
  tabIndex: isFocused ? 0 : -1,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\nimport {\n StyleMenuItemLabel,\n StyleMenuItemSecondaryLabel,\n StyledContentWrapper,\n StyledGlobalMenuItemWrapper,\n} from '@elliemae/ds-menu-items-commons';\nimport React from 'react';\nimport { useFloatingContext, type DSHookFloatingContextT } from '@elliemae/ds-floating-context';\nimport type { DSMenuButtonT } from '../../react-desc-prop-types.js';\nimport { MenuBehaviouralContextProviderContext } from '../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js';\nimport { MENU_FOCUS_REGIONS } from '../DSMenuBehaviouralContextProvider/constants/index.js';\nimport { type DSMenuItemRendererFactoryT } from './react-desc-prop-types.js';\nconst placementOrderPreference: Required<DSHookFloatingContextT.Props>['placementOrderPreference'] = [\n 'right-start',\n 'right-start',\n 'right',\n 'left-start',\n 'left-end',\n 'left',\n];\nexport const ActivableWithSubmenuMenuItem: React.ComponentType<{\n itemNode: DSMenuButtonT.MenuNodeActivableWithSubmenuItem;\n FlyoutMenuCircularDepInject: DSMenuItemRendererFactoryT.RequiredProps['FlyoutMenuCircularDepInject'];\n}> = ({ itemNode, FlyoutMenuCircularDepInject }) => {\n const { dsId, plainItem } = itemNode;\n const { label, secondaryLabel, leftDecoration: LeftDecComponent, minWidth, disabled } = plainItem;\n const floatingContext = useFloatingContext({\n placement: placementOrderPreference[0],\n placementOrderPreference,\n animationDuration: 100,\n customOffset: [0, 0],\n });\n const {\n refs: { setReference },\n } = floatingContext;\n\n const {\n focusRegion,\n openedSubItems,\n menuItemEventsHandlers: {\n handleFocusableMenuItemKeyDown,\n handleFocusableMenuItemClick,\n handleFocusableMenuItemOnMouseEnter,\n },\n } = React.useContext(MenuBehaviouralContextProviderContext);\n\n const gridLayout = React.useMemo(() => {\n const cols = LeftDecComponent ? ['min-content', 'auto'] : ['auto'];\n if (secondaryLabel) cols.push('auto');\n return cols;\n }, [LeftDecComponent, secondaryLabel]);\n\n const focusedRegionPerformanceHelper = React.useRef(focusRegion);\n focusedRegionPerformanceHelper.current = focusRegion;\n\n const isExpanded = openedSubItems.some((itemWithOpenSubmenu) => itemWithOpenSubmenu.dsId === dsId);\n const isFocused = focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId);\n\n const handleFocusOnRender = React.useCallback(\n (node: HTMLDivElement) => {\n setReference(node);\n setTimeout(() => {\n if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {\n node.focus();\n }\n });\n },\n // we need to change the callback reference every time the focusRegion changes or else the focus will not be set\n [dsId, focusRegion, setReference],\n // we are using the \"as='div'\", typescript is not able to infer the correct type\n // the logic here actually receives a ref to a HTMLDivElement,\n // but the component must think this is a HTMLLIElement ref callback\n ) as unknown as React.RefCallback<HTMLLIElement>;\n\n const handleOnMouseEnter = React.useCallback<React.MouseEventHandler<HTMLDivElement>>(\n (e) => {\n handleFocusableMenuItemOnMouseEnter(itemNode, e);\n },\n [handleFocusableMenuItemOnMouseEnter, itemNode],\n );\n\n const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\\s/g, '')}`;\n return (\n <>\n <StyledGlobalMenuItemWrapper\n innerRef={handleFocusOnRender}\n onKeyDown={handleFocusableMenuItemKeyDown}\n onClick={handleFocusableMenuItemClick}\n onMouseEnter={handleOnMouseEnter}\n as=\"div\"\n id={`${spacelessDsIdForDom}`}\n tabIndex={isFocused ? 0 : -1}\n role=\"menuitem\"\n aria-controls={\n /* ********************************************************************************************************************\n * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-controls\n * The aria-controls only needs to be set when the popup is visible, but it is valid and easier to program to reference an element that is not visible.\n * ********************************************************************************************************************\n * ^^^ some automated tools will mark an error if the aria-controls references an element that does (yet) exist in the DOM\n * so, while technically valid to not check if expanded, we are doing it to avoid automated tools marking an error\n * ******************************************************************************************************************** */\n isExpanded\n ? itemNode.plainChildren.map((child) => `ds-menu-item-${`${child.dsId}`.replace(/\\s/g, '')}`).join(' ')\n : undefined\n }\n aria-haspopup=\"menu\"\n aria-expanded={isExpanded}\n aria-disabled={disabled}\n applyAriaDisabled={disabled}\n >\n <StyledContentWrapper\n cols={gridLayout}\n minHeight=\"16px\"\n gutter=\"xxs\"\n alignItems=\"center\"\n minWidth={minWidth ?? undefined}\n >\n {LeftDecComponent ? <LeftDecComponent /> : null}\n <StyleMenuItemLabel>{label}</StyleMenuItemLabel>\n {secondaryLabel !== undefined && (\n <StyleMenuItemSecondaryLabel disabled={disabled}>{secondaryLabel}</StyleMenuItemSecondaryLabel>\n )}\n </StyledContentWrapper>\n </StyledGlobalMenuItemWrapper>\n <FlyoutMenuCircularDepInject\n isMenuOpen={isExpanded}\n floatingContext={floatingContext.context}\n floatingStyles={floatingContext.floatingStyles}\n setFloatingRef={floatingContext.refs.setFloating}\n itemNode={itemNode}\n />\n </>\n );\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\nimport {\n StyleMenuItemLabel,\n StyleMenuItemSecondaryLabel,\n StyledContentWrapper,\n StyledGlobalMenuItemWrapper,\n} from '@elliemae/ds-menu-items-commons';\nimport React from 'react';\nimport { useFloatingContext, type DSHookFloatingContextT } from '@elliemae/ds-floating-context';\nimport type { DSMenuButtonT } from '../../react-desc-prop-types.js';\nimport { MenuBehaviouralContextProviderContext } from '../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js';\nimport { MENU_FOCUS_REGIONS } from '../DSMenuBehaviouralContextProvider/constants/index.js';\nimport { type DSMenuItemRendererFactoryT } from './react-desc-prop-types.js';\nimport { focusNodeRacingConditionSolved } from './focusNodeRacingConditionSolved.js';\n\nconst placementOrderPreference: Required<DSHookFloatingContextT.Props>['placementOrderPreference'] = [\n 'right-start',\n 'right-start',\n 'right',\n 'left-start',\n 'left-end',\n 'left',\n];\nexport const ActivableWithSubmenuMenuItem: React.ComponentType<{\n itemNode: DSMenuButtonT.MenuNodeActivableWithSubmenuItem;\n FlyoutMenuCircularDepInject: DSMenuItemRendererFactoryT.RequiredProps['FlyoutMenuCircularDepInject'];\n}> = ({ itemNode, FlyoutMenuCircularDepInject }) => {\n const { dsId, plainItem } = itemNode;\n const { label, secondaryLabel, leftDecoration: LeftDecComponent, minWidth, disabled } = plainItem;\n const floatingContext = useFloatingContext({\n placement: placementOrderPreference[0],\n placementOrderPreference,\n animationDuration: 100,\n customOffset: [0, 0],\n });\n const {\n refs: { setReference },\n } = floatingContext;\n\n const {\n focusRegion,\n openedSubItems,\n menuItemEventsHandlers: {\n handleFocusableMenuItemKeyDown,\n handleFocusableMenuItemClick,\n handleFocusableMenuItemOnMouseEnter,\n handleFocusableMenuItemNativeFocusEvent,\n },\n } = React.useContext(MenuBehaviouralContextProviderContext);\n\n const gridLayout = React.useMemo(() => {\n const cols = LeftDecComponent ? ['min-content', 'auto'] : ['auto'];\n if (secondaryLabel) cols.push('auto');\n return cols;\n }, [LeftDecComponent, secondaryLabel]);\n\n const focusedRegionPerformanceHelper = React.useRef(focusRegion);\n focusedRegionPerformanceHelper.current = focusRegion;\n\n const isExpanded = openedSubItems.some((itemWithOpenSubmenu) => itemWithOpenSubmenu.dsId === dsId);\n const isFocused = focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId);\n\n const handleFocusOnRender = React.useCallback(\n (node: HTMLDivElement) => {\n setReference(node);\n if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {\n focusNodeRacingConditionSolved(node);\n }\n },\n // we need to change the callback reference every time the focusRegion changes or else the focus will not be set\n [dsId, focusRegion, setReference],\n // we are using the \"as='div'\", typescript is not able to infer the correct type\n // the logic here actually receives a ref to a HTMLDivElement,\n // but the component must think this is a HTMLLIElement ref callback\n ) as unknown as React.RefCallback<HTMLLIElement>;\n\n const handleOnMouseEnter = React.useCallback<React.MouseEventHandler<HTMLDivElement>>(() => {\n handleFocusableMenuItemOnMouseEnter(itemNode);\n }, [handleFocusableMenuItemOnMouseEnter, itemNode]);\n const handleOnFocus = React.useCallback<React.FocusEventHandler<HTMLDivElement>>(\n (e) => {\n handleFocusableMenuItemNativeFocusEvent(itemNode, e);\n },\n [handleFocusableMenuItemNativeFocusEvent, itemNode],\n );\n\n const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\\s/g, '')}`;\n return (\n <>\n <StyledGlobalMenuItemWrapper\n innerRef={handleFocusOnRender}\n onKeyDown={handleFocusableMenuItemKeyDown}\n onClick={handleFocusableMenuItemClick}\n onMouseEnter={handleOnMouseEnter}\n onFocus={handleOnFocus}\n as=\"div\"\n id={`${spacelessDsIdForDom}`}\n tabIndex={isFocused ? 0 : -1}\n role=\"menuitem\"\n aria-controls={\n /* ********************************************************************************************************************\n * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-controls\n * The aria-controls only needs to be set when the popup is visible, but it is valid and easier to program to reference an element that is not visible.\n * ********************************************************************************************************************\n * ^^^ some automated tools will mark an error if the aria-controls references an element that does (yet) exist in the DOM\n * so, while technically valid to not check if expanded, we are doing it to avoid automated tools marking an error\n * ******************************************************************************************************************** */\n isExpanded\n ? itemNode.plainChildren.map((child) => `ds-menu-item-${`${child.dsId}`.replace(/\\s/g, '')}`).join(' ')\n : undefined\n }\n aria-haspopup=\"menu\"\n aria-expanded={isExpanded}\n aria-disabled={disabled}\n applyAriaDisabled={disabled}\n >\n <StyledContentWrapper\n cols={gridLayout}\n minHeight=\"16px\"\n gutter=\"xxs\"\n alignItems=\"center\"\n minWidth={minWidth ?? undefined}\n >\n {LeftDecComponent ? <LeftDecComponent /> : null}\n <StyleMenuItemLabel>{label}</StyleMenuItemLabel>\n {secondaryLabel !== undefined && (\n <StyleMenuItemSecondaryLabel disabled={disabled}>{secondaryLabel}</StyleMenuItemSecondaryLabel>\n )}\n </StyledContentWrapper>\n </StyledGlobalMenuItemWrapper>\n <FlyoutMenuCircularDepInject\n isMenuOpen={isExpanded}\n floatingContext={floatingContext.context}\n floatingStyles={floatingContext.floatingStyles}\n setFloatingRef={floatingContext.refs.setFloating}\n itemNode={itemNode}\n />\n </>\n );\n};\n"],
5
+ "mappings": "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",
6
6
  "names": ["React"]
7
7
  }
@@ -1,31 +1,32 @@
1
1
  import * as React from "react";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
3
  import { Grid } from "@elliemae/ds-grid";
4
- import { describe } from "@elliemae/ds-props-helpers";
5
4
  import { StyledGroupLabel, StyledSeparator, StyledSeparatorWrapper } from "@elliemae/ds-menu-items-commons";
5
+ import { describe } from "@elliemae/ds-props-helpers";
6
6
  import { styled } from "@elliemae/ds-system";
7
- import { useMenuItemRendererFactory } from "./config/useMenuItemRendererFactory.js";
8
- import { DSMenuItemRendererFactoryName, MENU_ITEM_RENDERER_FACTORY_SLOTS } from "./constants/index.js";
9
- import { DSMenuItemRendererFactoryPropTypesSchema } from "./react-desc-prop-types.js";
10
- import { ActivableMenuItem } from "./ActivableMenuItem.js";
11
- import { SingleSelectMenuItem } from "./SingleSelectMenuItem.js";
12
- import { SingleSelectWithSubmenuMenuItem } from "./SingleSelectWithSubmenuMenuItem.js";
13
- import { MultipleSelectMenuItem } from "./MultipleSelectMenuItem.js";
14
- import { WithSubmenuMenuItem } from "./WithSubmenuMenuItem.js";
15
- import { MultipleSelectWithSubmenuMenuItem } from "./MultipleSelectWithSubmenuMenuItem.js";
16
- import { ActivableWithSubmenuMenuItem } from "./ActivableWithSubmenuMenuItem.js";
17
7
  import {
18
8
  isActivableNode,
19
- isSkeletonNode,
9
+ isActivableWithSubmenuNode,
10
+ isGroup,
20
11
  isMultipleSelectOnlyNode,
21
- isSingleSelectOnlyNode,
12
+ isMultipleSelectWithSubmenuNode,
22
13
  isSeparatorNode,
23
- isGroup,
24
- isWithSubmenuOnlyNode,
25
14
  isSingleSelectNodeWithSubmenu,
26
- isMultipleSelectWithSubmenuNode,
27
- isActivableWithSubmenuNode
15
+ isSingleSelectOnlyNode,
16
+ isSkeletonNode,
17
+ isWithSubmenuOnlyNode
28
18
  } from "../../utils/nodesTypeguardsAndGetters.js";
19
+ import { ActivableMenuItem } from "./ActivableMenuItem.js";
20
+ import { ActivableWithSubmenuMenuItem } from "./ActivableWithSubmenuMenuItem.js";
21
+ import { MultipleSelectMenuItem } from "./MultipleSelectMenuItem.js";
22
+ import { MultipleSelectWithSubmenuMenuItem } from "./MultipleSelectWithSubmenuMenuItem.js";
23
+ import { SingleSelectMenuItem } from "./SingleSelectMenuItem.js";
24
+ import { SingleSelectWithSubmenuMenuItem } from "./SingleSelectWithSubmenuMenuItem.js";
25
+ import { SkeletonMenuItem } from "./SkeletonMenuItem.js";
26
+ import { WithSubmenuMenuItem } from "./WithSubmenuMenuItem.js";
27
+ import { useMenuItemRendererFactory } from "./config/useMenuItemRendererFactory.js";
28
+ import { DSMenuItemRendererFactoryName, MENU_ITEM_RENDERER_FACTORY_SLOTS } from "./constants/index.js";
29
+ import { DSMenuItemRendererFactoryPropTypesSchema } from "./react-desc-prop-types.js";
29
30
  const StyledGroupLabelWrapper = styled(Grid, {
30
31
  name: DSMenuItemRendererFactoryName,
31
32
  slot: MENU_ITEM_RENDERER_FACTORY_SLOTS.GROUP_LABEL_WRAPPER
@@ -44,7 +45,7 @@ const DSMenuItemRendererFactory = (props) => {
44
45
  if (ItemRenderer) return /* @__PURE__ */ jsx(ItemRenderer, { itemNode });
45
46
  if (isSeparatorNode(itemNode))
46
47
  return /* @__PURE__ */ jsx(StyledSeparatorWrapper, { as: "div", role: "presentation", children: /* @__PURE__ */ jsx(StyledSeparator, {}) });
47
- if (isSkeletonNode(itemNode)) return /* @__PURE__ */ jsx("li", { children: "loading..." });
48
+ if (isSkeletonNode(itemNode)) return /* @__PURE__ */ jsx(SkeletonMenuItem, { itemNode });
48
49
  if (isActivableNode(itemNode)) return /* @__PURE__ */ jsx(ActivableMenuItem, { itemNode });
49
50
  if (isMultipleSelectOnlyNode(itemNode)) return /* @__PURE__ */ jsx(MultipleSelectMenuItem, { itemNode });
50
51
  if (isSingleSelectOnlyNode(itemNode)) return /* @__PURE__ */ jsx(SingleSelectMenuItem, { itemNode });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/DSMenuItemRendererFactory/DSMenuItemRendererFactory.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\nimport { Grid } from '@elliemae/ds-grid';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { StyledGroupLabel, StyledSeparator, StyledSeparatorWrapper } from '@elliemae/ds-menu-items-commons';\nimport { styled } from '@elliemae/ds-system';\nimport React from 'react';\nimport { useMenuItemRendererFactory } from './config/useMenuItemRendererFactory.js';\nimport { DSMenuItemRendererFactoryName, MENU_ITEM_RENDERER_FACTORY_SLOTS } from './constants/index.js';\nimport { DSMenuItemRendererFactoryPropTypesSchema, type DSMenuItemRendererFactoryT } from './react-desc-prop-types.js';\nimport { ActivableMenuItem } from './ActivableMenuItem.js';\nimport { SingleSelectMenuItem } from './SingleSelectMenuItem.js';\nimport { SingleSelectWithSubmenuMenuItem } from './SingleSelectWithSubmenuMenuItem.js';\nimport { MultipleSelectMenuItem } from './MultipleSelectMenuItem.js';\nimport { WithSubmenuMenuItem } from './WithSubmenuMenuItem.js';\nimport { MultipleSelectWithSubmenuMenuItem } from './MultipleSelectWithSubmenuMenuItem.js';\nimport { ActivableWithSubmenuMenuItem } from './ActivableWithSubmenuMenuItem.js';\nimport {\n isActivableNode,\n isSkeletonNode,\n isMultipleSelectOnlyNode,\n isSingleSelectOnlyNode,\n isSeparatorNode,\n isGroup,\n isWithSubmenuOnlyNode,\n isSingleSelectNodeWithSubmenu,\n isMultipleSelectWithSubmenuNode,\n isActivableWithSubmenuNode,\n} from '../../utils/nodesTypeguardsAndGetters.js';\n\nconst StyledGroupLabelWrapper = styled(Grid, {\n name: DSMenuItemRendererFactoryName,\n slot: MENU_ITEM_RENDERER_FACTORY_SLOTS.GROUP_LABEL_WRAPPER,\n})`\n padding: ${({ theme }) => theme.space.xxxs} ${({ theme }) => theme.space.xs};\n`;\n\nconst StyledLabelWithLeftDecWrapper = styled(Grid, {\n name: DSMenuItemRendererFactoryName,\n slot: MENU_ITEM_RENDERER_FACTORY_SLOTS.LABEL_WITH_LEFT_DEC_WRAPPER,\n})`\n padding-left: ${({ theme }) => theme.space.xxs};\n`;\n\nconst DSMenuItemRendererFactory: React.ComponentType<DSMenuItemRendererFactoryT.Props> = (props) => {\n const { propsWithDefault, instanceUid } = useMenuItemRendererFactory(props);\n const { itemNode, ItemRenderer, FlyoutMenuCircularDepInject } = propsWithDefault;\n\n if (ItemRenderer) return <ItemRenderer itemNode={itemNode} />;\n\n if (isSeparatorNode(itemNode))\n return (\n <StyledSeparatorWrapper as=\"div\" role=\"presentation\">\n <StyledSeparator />\n </StyledSeparatorWrapper>\n );\n if (isSkeletonNode(itemNode)) return <li>loading...</li>;\n if (isActivableNode(itemNode)) return <ActivableMenuItem itemNode={itemNode} />;\n if (isMultipleSelectOnlyNode(itemNode)) return <MultipleSelectMenuItem itemNode={itemNode} />;\n if (isSingleSelectOnlyNode(itemNode)) return <SingleSelectMenuItem itemNode={itemNode} />;\n\n if (isWithSubmenuOnlyNode(itemNode))\n return <WithSubmenuMenuItem itemNode={itemNode} FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject} />;\n if (isSingleSelectNodeWithSubmenu(itemNode))\n return (\n <SingleSelectWithSubmenuMenuItem itemNode={itemNode} FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject} />\n );\n if (isMultipleSelectWithSubmenuNode(itemNode))\n return (\n <MultipleSelectWithSubmenuMenuItem\n itemNode={itemNode}\n FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject}\n />\n );\n if (isActivableWithSubmenuNode(itemNode))\n return (\n <ActivableWithSubmenuMenuItem itemNode={itemNode} FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject} />\n );\n\n // defined in this file to avoid to have to inject the ItemRendererFactory prop in the \"GroupMenuItem\" component\n // if we eventually move this to a separate file,\n // we will have to inject the ItemRendererFactory prop in the \"GroupMenuItem\" component to avoid circular dependencies\n if (isGroup(itemNode)) {\n const { leftDecoration: LeftDecComponent, label, minWidth } = itemNode.plainItem;\n const spacelessIdForGroup = label ? `label-${label.replace(/\\s/g, '')}` : undefined;\n\n return (\n <div role=\"group\" aria-labelledby={spacelessIdForGroup} style={minWidth ? { minWidth } : {}}>\n {label ? (\n <StyledGroupLabelWrapper cols={LeftDecComponent ? ['min-content', '1fr'] : ['1fr']}>\n {LeftDecComponent ? <LeftDecComponent /> : null}\n <StyledGroupLabel role=\"presentation\" id={spacelessIdForGroup}>\n {LeftDecComponent ? <StyledLabelWithLeftDecWrapper>{label}</StyledLabelWithLeftDecWrapper> : label}\n </StyledGroupLabel>\n </StyledGroupLabelWrapper>\n ) : null}\n {itemNode.children.map((subItemNode) => (\n <DSMenuItemRendererFactory\n key={`item-factory-${subItemNode.dsId}-${instanceUid}`}\n itemNode={subItemNode}\n FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject}\n />\n ))}\n </div>\n );\n }\n\n // if we get here either\n // - a new type of item has been added and this component wasn't updated\n // -> update this component to handle the new type\n // - developer provided an invalid type for the item\n // -> developer should fix the item type\n // ----------\n // the following rule is disabled because this is LITERALLY an error throwing section,\n // of course typescript thinks type is never, that's why we are throwing an error...\n // ----------\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n throw new Error(`Invalid item type: ${JSON.stringify(itemNode)}`);\n};\n\nDSMenuItemRendererFactory.displayName = DSMenuItemRendererFactoryName;\nconst DSMenuItemRendererFactoryWithSchema = describe(DSMenuItemRendererFactory);\nDSMenuItemRendererFactoryWithSchema.propTypes = DSMenuItemRendererFactoryPropTypesSchema;\n\nexport { DSMenuItemRendererFactory, DSMenuItemRendererFactoryWithSchema };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable complexity */\nimport { Grid } from '@elliemae/ds-grid';\nimport { StyledGroupLabel, StyledSeparator, StyledSeparatorWrapper } from '@elliemae/ds-menu-items-commons';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { styled } from '@elliemae/ds-system';\nimport React from 'react';\nimport {\n isActivableNode,\n isActivableWithSubmenuNode,\n isGroup,\n isMultipleSelectOnlyNode,\n isMultipleSelectWithSubmenuNode,\n isSeparatorNode,\n isSingleSelectNodeWithSubmenu,\n isSingleSelectOnlyNode,\n isSkeletonNode,\n isWithSubmenuOnlyNode,\n} from '../../utils/nodesTypeguardsAndGetters.js';\nimport { ActivableMenuItem } from './ActivableMenuItem.js';\nimport { ActivableWithSubmenuMenuItem } from './ActivableWithSubmenuMenuItem.js';\nimport { MultipleSelectMenuItem } from './MultipleSelectMenuItem.js';\nimport { MultipleSelectWithSubmenuMenuItem } from './MultipleSelectWithSubmenuMenuItem.js';\nimport { SingleSelectMenuItem } from './SingleSelectMenuItem.js';\nimport { SingleSelectWithSubmenuMenuItem } from './SingleSelectWithSubmenuMenuItem.js';\nimport { SkeletonMenuItem } from './SkeletonMenuItem.js';\nimport { WithSubmenuMenuItem } from './WithSubmenuMenuItem.js';\nimport { useMenuItemRendererFactory } from './config/useMenuItemRendererFactory.js';\nimport { DSMenuItemRendererFactoryName, MENU_ITEM_RENDERER_FACTORY_SLOTS } from './constants/index.js';\nimport { DSMenuItemRendererFactoryPropTypesSchema, type DSMenuItemRendererFactoryT } from './react-desc-prop-types.js';\n\nconst StyledGroupLabelWrapper = styled(Grid, {\n name: DSMenuItemRendererFactoryName,\n slot: MENU_ITEM_RENDERER_FACTORY_SLOTS.GROUP_LABEL_WRAPPER,\n})`\n padding: ${({ theme }) => theme.space.xxxs} ${({ theme }) => theme.space.xs};\n`;\n\nconst StyledLabelWithLeftDecWrapper = styled(Grid, {\n name: DSMenuItemRendererFactoryName,\n slot: MENU_ITEM_RENDERER_FACTORY_SLOTS.LABEL_WITH_LEFT_DEC_WRAPPER,\n})`\n padding-left: ${({ theme }) => theme.space.xxs};\n`;\n\nconst DSMenuItemRendererFactory: React.ComponentType<DSMenuItemRendererFactoryT.Props> = (props) => {\n const { propsWithDefault, instanceUid } = useMenuItemRendererFactory(props);\n const { itemNode, ItemRenderer, FlyoutMenuCircularDepInject } = propsWithDefault;\n\n if (ItemRenderer) return <ItemRenderer itemNode={itemNode} />;\n\n if (isSeparatorNode(itemNode))\n return (\n <StyledSeparatorWrapper as=\"div\" role=\"presentation\">\n <StyledSeparator />\n </StyledSeparatorWrapper>\n );\n if (isSkeletonNode(itemNode)) return <SkeletonMenuItem itemNode={itemNode} />;\n if (isActivableNode(itemNode)) return <ActivableMenuItem itemNode={itemNode} />;\n if (isMultipleSelectOnlyNode(itemNode)) return <MultipleSelectMenuItem itemNode={itemNode} />;\n if (isSingleSelectOnlyNode(itemNode)) return <SingleSelectMenuItem itemNode={itemNode} />;\n\n if (isWithSubmenuOnlyNode(itemNode))\n return <WithSubmenuMenuItem itemNode={itemNode} FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject} />;\n if (isSingleSelectNodeWithSubmenu(itemNode))\n return (\n <SingleSelectWithSubmenuMenuItem itemNode={itemNode} FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject} />\n );\n if (isMultipleSelectWithSubmenuNode(itemNode))\n return (\n <MultipleSelectWithSubmenuMenuItem\n itemNode={itemNode}\n FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject}\n />\n );\n if (isActivableWithSubmenuNode(itemNode))\n return (\n <ActivableWithSubmenuMenuItem itemNode={itemNode} FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject} />\n );\n\n // defined in this file to avoid to have to inject the ItemRendererFactory prop in the \"GroupMenuItem\" component\n // if we eventually move this to a separate file,\n // we will have to inject the ItemRendererFactory prop in the \"GroupMenuItem\" component to avoid circular dependencies\n if (isGroup(itemNode)) {\n const { leftDecoration: LeftDecComponent, label, minWidth } = itemNode.plainItem;\n const spacelessIdForGroup = label ? `label-${label.replace(/\\s/g, '')}` : undefined;\n\n return (\n <div role=\"group\" aria-labelledby={spacelessIdForGroup} style={minWidth ? { minWidth } : {}}>\n {label ? (\n <StyledGroupLabelWrapper cols={LeftDecComponent ? ['min-content', '1fr'] : ['1fr']}>\n {LeftDecComponent ? <LeftDecComponent /> : null}\n <StyledGroupLabel role=\"presentation\" id={spacelessIdForGroup}>\n {LeftDecComponent ? <StyledLabelWithLeftDecWrapper>{label}</StyledLabelWithLeftDecWrapper> : label}\n </StyledGroupLabel>\n </StyledGroupLabelWrapper>\n ) : null}\n {itemNode.children.map((subItemNode) => (\n <DSMenuItemRendererFactory\n key={`item-factory-${subItemNode.dsId}-${instanceUid}`}\n itemNode={subItemNode}\n FlyoutMenuCircularDepInject={FlyoutMenuCircularDepInject}\n />\n ))}\n </div>\n );\n }\n\n // if we get here either\n // - a new type of item has been added and this component wasn't updated\n // -> update this component to handle the new type\n // - developer provided an invalid type for the item\n // -> developer should fix the item type\n // ----------\n // the following rule is disabled because this is LITERALLY an error throwing section,\n // of course typescript thinks type is never, that's why we are throwing an error...\n // ----------\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n throw new Error(`Invalid item type: ${JSON.stringify(itemNode)}`);\n};\n\nDSMenuItemRendererFactory.displayName = DSMenuItemRendererFactoryName;\nconst DSMenuItemRendererFactoryWithSchema = describe(DSMenuItemRendererFactory);\nDSMenuItemRendererFactoryWithSchema.propTypes = DSMenuItemRendererFactoryPropTypesSchema;\n\nexport { DSMenuItemRendererFactory, DSMenuItemRendererFactoryWithSchema };\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -11,6 +11,7 @@ import {
11
11
  import React2 from "react";
12
12
  import { MenuBehaviouralContextProviderContext } from "../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js";
13
13
  import { MENU_FOCUS_REGIONS } from "../DSMenuBehaviouralContextProvider/constants/index.js";
14
+ import { focusNodeRacingConditionSolved } from "./focusNodeRacingConditionSolved.js";
14
15
  const LeftBoxlessCheckbox = React2.memo(({ isSelected }) => /* @__PURE__ */ jsx(Grid, { width: "16px", children: isSelected ? /* @__PURE__ */ jsx(Checkmark, { size: "s", color: ["brand-primary", "600"] }) : /* @__PURE__ */ jsx("div", {}) }));
15
16
  const MultipleSelectMenuItem = ({ itemNode }) => {
16
17
  const { dsId, plainItem } = itemNode;
@@ -21,10 +22,11 @@ const MultipleSelectMenuItem = ({ itemNode }) => {
21
22
  menuItemEventsHandlers: {
22
23
  handleFocusableMenuItemKeyDown,
23
24
  handleFocusableMenuItemClick,
24
- handleFocusableMenuItemOnMouseEnter
25
+ handleFocusableMenuItemOnMouseEnter,
26
+ handleFocusableMenuItemNativeFocusEvent
25
27
  }
26
28
  } = React2.useContext(MenuBehaviouralContextProviderContext);
27
- const { selectedItems } = propsWithDefault;
29
+ const { selectedNodes } = propsWithDefault;
28
30
  const gridLayout = React2.useMemo(() => {
29
31
  const cols = LeftDecComponent ? ["min-content", "min-content", "auto"] : ["min-content", "auto"];
30
32
  if (secondaryLabel) cols.push("auto");
@@ -33,14 +35,12 @@ const MultipleSelectMenuItem = ({ itemNode }) => {
33
35
  const focusedRegionPerformanceHelper = React2.useRef(focusRegion);
34
36
  focusedRegionPerformanceHelper.current = focusRegion;
35
37
  const isFocused = focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId);
36
- const isSelected = selectedItems.some((itemMarkesAsSelected) => itemMarkesAsSelected.dsId === dsId);
38
+ const isSelected = selectedNodes.some((itemMarkesAsSelected) => itemMarkesAsSelected.dsId === dsId);
37
39
  const handleFocusOnRender = React2.useCallback(
38
40
  (node) => {
39
- setTimeout(() => {
40
- if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {
41
- node.focus();
42
- }
43
- });
41
+ if (node && focusRegion === MENU_FOCUS_REGIONS.ITEM_BY_DSID(dsId)) {
42
+ focusNodeRacingConditionSolved(node);
43
+ }
44
44
  },
45
45
  // we need to change the callback reference every time the focusRegion changes or else the focus will not be set
46
46
  [dsId, focusRegion]
@@ -48,11 +48,14 @@ const MultipleSelectMenuItem = ({ itemNode }) => {
48
48
  // the logic here actually receives a ref to a HTMLDivElement,
49
49
  // but the component must think this is a HTMLLIElement ref callback
50
50
  );
51
- const handleOnMouseEnter = React2.useCallback(
51
+ const handleOnMouseEnter = React2.useCallback(() => {
52
+ handleFocusableMenuItemOnMouseEnter(itemNode);
53
+ }, [handleFocusableMenuItemOnMouseEnter, itemNode]);
54
+ const handleOnFocus = React2.useCallback(
52
55
  (e) => {
53
- handleFocusableMenuItemOnMouseEnter(itemNode, e);
56
+ handleFocusableMenuItemNativeFocusEvent(itemNode, e);
54
57
  },
55
- [handleFocusableMenuItemOnMouseEnter, itemNode]
58
+ [handleFocusableMenuItemNativeFocusEvent, itemNode]
56
59
  );
57
60
  const spacelessDsIdForDom = `ds-menu-item-${`${dsId}`.replace(/\s/g, "")}`;
58
61
  return /* @__PURE__ */ jsx(
@@ -62,6 +65,7 @@ const MultipleSelectMenuItem = ({ itemNode }) => {
62
65
  onKeyDown: handleFocusableMenuItemKeyDown,
63
66
  onClick: handleFocusableMenuItemClick,
64
67
  onMouseEnter: handleOnMouseEnter,
68
+ onFocus: handleOnFocus,
65
69
  as: "div",
66
70
  id: `${spacelessDsIdForDom}`,
67
71
  tabIndex: isFocused ? 0 : -1,