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

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
@@ -34,21 +34,28 @@ module.exports = __toCommonJS(useOptionsArrayToDsTree_exports);
34
34
  var React = __toESM(require("react"));
35
35
  var import_react = __toESM(require("react"));
36
36
  var import_ds_tree_model = require("@elliemae/ds-tree-model");
37
+ var import_react_desc_prop_types = require("../react-desc-prop-types.js");
37
38
  const useDsTreeOpts = {
38
39
  getUniqueId: (item) => item.dsId
39
40
  };
40
- const useOptionsArrayToDsTree = ({ options, instanceUid }) => {
41
+ const useOptionsArrayToDsTree = ({
42
+ options,
43
+ instanceUid,
44
+ propsWithDefault
45
+ }) => {
46
+ const { maxHeight } = propsWithDefault || import_react_desc_prop_types.menuSpecificDefaultProps;
41
47
  const rootNode = import_react.default.useMemo(
42
48
  () => ({
43
49
  dsId: `root-${instanceUid}`,
44
50
  type: "ROOT_ITEM",
45
- subitems: options
51
+ subitems: options,
52
+ maxHeight
46
53
  // as unknown as RootNodeType is because currently
47
54
  // the DS tree model doesn't support the type of the root node being different from the subitems
48
55
  // in practice, this is fine because the root node is never rendered & the subitems are the only thing that matters
49
56
  // will be addresssed via PUI-XXXX
50
57
  }),
51
- [instanceUid, options]
58
+ [instanceUid, maxHeight, options]
52
59
  );
53
60
  return (0, import_ds_tree_model.useDSTree)(rootNode, useDsTreeOpts);
54
61
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/useOptionsArrayToDsTree.ts", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["import React from 'react';\nimport { useDSTree } from '@elliemae/ds-tree-model';\nimport type { UseDSTreeT } from '@elliemae/ds-tree-model';\nimport { type DSMenuButtonT } from '../react-desc-prop-types.js';\n\nconst useDsTreeOpts: UseDSTreeT.SecondParameter<DSMenuButtonT.MenuItemInterface> = {\n getUniqueId: (item) => item.dsId,\n};\n\ntype UseOptionsArrayToDsTreeRootNodeConfig = {\n options: DSMenuButtonT.MenuItemInterface[];\n instanceUid: string;\n};\n\ntype RootNodeType = UseDSTreeT.Item<DSMenuButtonT.MenuItemInterface>;\n\nexport const useOptionsArrayToDsTree = ({ options, instanceUid }: UseOptionsArrayToDsTreeRootNodeConfig) => {\n // we need to create the root node that the app dev doesn't need to worry about\n const rootNode = React.useMemo<RootNodeType>(\n () =>\n ({\n dsId: `root-${instanceUid}`,\n type: 'ROOT_ITEM',\n subitems: options,\n // as unknown as RootNodeType is because currently\n // the DS tree model doesn't support the type of the root node being different from the subitems\n // in practice, this is fine because the root node is never rendered & the subitems are the only thing that matters\n // will be addresssed via PUI-XXXX\n }) as unknown as RootNodeType,\n [instanceUid, options],\n );\n return useDSTree<DSMenuButtonT.MenuItemInterface>(rootNode, useDsTreeOpts);\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAkB;AAClB,2BAA0B;AAI1B,MAAM,gBAA6E;AAAA,EACjF,aAAa,CAAC,SAAS,KAAK;AAC9B;AASO,MAAM,0BAA0B,CAAC,EAAE,SAAS,YAAY,MAA6C;AAE1G,QAAM,WAAW,aAAAA,QAAM;AAAA,IACrB,OACG;AAAA,MACC,MAAM,QAAQ,WAAW;AAAA,MACzB,MAAM;AAAA,MACN,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,IAKZ;AAAA,IACF,CAAC,aAAa,OAAO;AAAA,EACvB;AACA,aAAO,gCAA2C,UAAU,aAAa;AAC3E;",
4
+ "sourcesContent": ["import React from 'react';\nimport { useDSTree } from '@elliemae/ds-tree-model';\nimport type { UseDSTreeT } from '@elliemae/ds-tree-model';\nimport { type DSMenuButtonT, menuSpecificDefaultProps } from '../react-desc-prop-types.js';\n\nconst useDsTreeOpts: UseDSTreeT.SecondParameter<DSMenuButtonT.MenuItemInterface> = {\n getUniqueId: (item) => item.dsId,\n};\n\ntype UseOptionsArrayToDsTreeRootNodeConfig = {\n options: DSMenuButtonT.MenuItemInterface[];\n instanceUid: string;\n propsWithDefault?: DSMenuButtonT.MenuSpecifiDefaultProps;\n};\n\ntype RootNodeType = UseDSTreeT.Item<DSMenuButtonT.MenuItemInterface>;\n\nexport const useOptionsArrayToDsTree = ({\n options,\n instanceUid,\n propsWithDefault,\n}: UseOptionsArrayToDsTreeRootNodeConfig) => {\n const { maxHeight } = propsWithDefault || menuSpecificDefaultProps;\n // we need to create the root node that the app dev doesn't need to worry about\n const rootNode = React.useMemo<RootNodeType>(\n () =>\n ({\n dsId: `root-${instanceUid}`,\n type: 'ROOT_ITEM',\n subitems: options,\n maxHeight,\n // as unknown as RootNodeType is because currently\n // the DS tree model doesn't support the type of the root node being different from the subitems\n // in practice, this is fine because the root node is never rendered & the subitems are the only thing that matters\n // will be addresssed via PUI-XXXX\n }) as unknown as RootNodeType,\n [instanceUid, maxHeight, options],\n );\n return useDSTree<DSMenuButtonT.MenuItemInterface>(rootNode, useDsTreeOpts);\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAkB;AAClB,2BAA0B;AAE1B,mCAA6D;AAE7D,MAAM,gBAA6E;AAAA,EACjF,aAAa,CAAC,SAAS,KAAK;AAC9B;AAUO,MAAM,0BAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,MAA6C;AAC3C,QAAM,EAAE,UAAU,IAAI,oBAAoB;AAE1C,QAAM,WAAW,aAAAA,QAAM;AAAA,IACrB,OACG;AAAA,MACC,MAAM,QAAQ,WAAW;AAAA,MACzB,MAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA;AAAA;AAAA;AAAA;AAAA,IAKF;AAAA,IACF,CAAC,aAAa,WAAW,OAAO;AAAA,EAClC;AACA,aAAO,gCAA2C,UAAU,aAAa;AAC3E;",
6
6
  "names": ["React"]
7
7
  }
@@ -1,14 +1,17 @@
1
1
  import * as React from "react";
2
- import React2 from "react";
3
2
  import { useGetXstyledProps, useMemoMergePropsWithDefault } from "@elliemae/ds-props-helpers";
3
+ import React2 from "react";
4
4
  import { uid } from "uid";
5
- import { DSMenuButtonPropTypes, defaultProps } from "../react-desc-prop-types.js";
6
- import { useValidateProps } from "./useValidateProps.js";
7
- import { useSplitInherithedProps } from "./useSplitInherithedProps.js";
8
- import { useOptionsArrayToDsTree } from "../utils/useOptionsArrayToDsTree.js";
5
+ import { DSMenuButtonPropTypes, menuSpecificDefaultProps } from "../react-desc-prop-types.js";
9
6
  import { getFocusableNodes } from "../utils/nodesTypeguardsAndGetters.js";
7
+ import { useOptionsArrayToDsTree } from "../utils/useOptionsArrayToDsTree.js";
8
+ import { useSplitInherithedProps } from "./useSplitInherithedProps.js";
9
+ import { useValidateProps } from "./useValidateProps.js";
10
10
  const useMenuButton = (propsFromUser) => {
11
- const propsWithDefault = useMemoMergePropsWithDefault(propsFromUser, defaultProps);
11
+ const propsWithDefault = useMemoMergePropsWithDefault(
12
+ propsFromUser,
13
+ menuSpecificDefaultProps
14
+ );
12
15
  useValidateProps(propsWithDefault, DSMenuButtonPropTypes);
13
16
  const xstyledProps = useGetXstyledProps(propsWithDefault);
14
17
  const instanceUid = React2.useMemo(() => `menu-button${uid(5)}`, []);
@@ -16,7 +19,8 @@ const useMenuButton = (propsFromUser) => {
16
19
  const optionsArray = Array.isArray(appOptions) ? appOptions : [];
17
20
  const treeRootNodeFromOptionsArrayIfArray = useOptionsArrayToDsTree({
18
21
  options: optionsArray,
19
- instanceUid
22
+ instanceUid,
23
+ propsWithDefault
20
24
  // this typecast is a safe type reconciliation, typescript checks they are compatible
21
25
  // this typecast let us avoid to have to do the ` | OtherCompatibleType` dance
22
26
  }).getRoot();
@@ -30,19 +34,6 @@ const useMenuButton = (propsFromUser) => {
30
34
  focusableNodes,
31
35
  treeRootNode
32
36
  });
33
- const allNodes = treeRootNode.flatten();
34
- const nodesWithRepeatedDsId = allNodes.filter(
35
- (node, index, self) => self.findIndex((n) => n.dsId === node.dsId) !== index
36
- );
37
- if (nodesWithRepeatedDsId.length > 0) {
38
- throw new Error(
39
- `DSMenuButton:
40
- The following dsId are repeated:
41
- ${nodesWithRepeatedDsId.map((n) => n.dsId).join("\n ")}
42
-
43
- The dsId must be unique.`
44
- );
45
- }
46
37
  return React2.useMemo(
47
38
  () => ({
48
39
  propsWithDefault,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/config/useMenuButton.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import React from 'react';\nimport { useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport { uid } from 'uid';\nimport { type DSMenuButtonT, DSMenuButtonPropTypes, defaultProps } from '../react-desc-prop-types.js';\nimport { useValidateProps } from './useValidateProps.js';\nimport { useSplitInherithedProps } from './useSplitInherithedProps.js';\nimport { useOptionsArrayToDsTree } from '../utils/useOptionsArrayToDsTree.js';\nimport { getFocusableNodes } from '../utils/nodesTypeguardsAndGetters.js';\nexport interface MenuButtonCTX extends ReturnType<typeof useSplitInherithedProps> {\n propsWithDefault: DSMenuButtonT.InternalProps;\n xstyledProps: ReturnType<typeof useGetXstyledProps>;\n instanceUid: string;\n}\n\nexport const useMenuButton = (propsFromUser: DSMenuButtonT.Props) => {\n // =============================================================================\n // MERGE WITH DEFAULT AND VALIDATE PROPS\n // =============================================================================\n const propsWithDefault = useMemoMergePropsWithDefault<DSMenuButtonT.InternalProps>(propsFromUser, defaultProps);\n useValidateProps(propsWithDefault, DSMenuButtonPropTypes);\n // =============================================================================\n // XSTYLED PROPS\n // =============================================================================\n const xstyledProps = useGetXstyledProps(propsWithDefault);\n // =============================================================================\n // AD HOC PER COMPONENT LOGIC\n // =============================================================================\n // custom code goes here, this is an example\n const instanceUid = React.useMemo(() => `menu-button${uid(5)}`, []);\n // =============================================================================\n // HELPERS HOOKS CONFIGS\n // =============================================================================\n\n // =============================================================================\n // React hooks can't be invoked conditionally...\n // =============================================================================\n const { options: appOptions } = propsWithDefault;\n const optionsArray: DSMenuButtonT.MenuItemInterface[] = Array.isArray(appOptions) ? appOptions : [];\n\n const treeRootNodeFromOptionsArrayIfArray = useOptionsArrayToDsTree({\n options: optionsArray,\n instanceUid,\n // this typecast is a safe type reconciliation, typescript checks they are compatible\n // this typecast let us avoid to have to do the ` | OtherCompatibleType` dance\n }).getRoot() as DSMenuButtonT.MenuNode;\n const treeRootNode = React.useMemo(\n () => (Array.isArray(appOptions) ? treeRootNodeFromOptionsArrayIfArray : appOptions),\n [appOptions, treeRootNodeFromOptionsArrayIfArray],\n );\n\n // =============================================================================\n // We calculate the focusableNodes from the treeRootNode.plainChildren via getFocusableNodes\n // =============================================================================\n const focusableNodes = React.useMemo(() => getFocusableNodes(treeRootNode), [treeRootNode]);\n const splitInherithedProps = useSplitInherithedProps({\n propsWithDefault,\n focusableNodes,\n treeRootNode,\n });\n\n // =============================================================================\n // if any item as a repeated dsId, we must throw an error,\n // the component will not work properly if the dsId is not guaranteed to be unique\n // (only validating focusableNodes may work, but as a good practice it's better to validate the whole tree)\n // =============================================================================\n const allNodes = treeRootNode.flatten();\n const nodesWithRepeatedDsId = allNodes.filter(\n (node, index, self) => self.findIndex((n) => n.dsId === node.dsId) !== index,\n );\n if (nodesWithRepeatedDsId.length > 0) {\n throw new Error(\n `DSMenuButton:\\nThe following dsId are repeated:\\n${nodesWithRepeatedDsId\n .map((n) => n.dsId)\n .join('\\n ')}\\n\\nThe dsId must be unique.`,\n );\n }\n\n return React.useMemo<MenuButtonCTX>(\n () => ({\n propsWithDefault,\n xstyledProps,\n instanceUid,\n ...splitInherithedProps,\n }),\n [propsWithDefault, xstyledProps, instanceUid, splitInherithedProps],\n );\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACAvB,OAAOA,YAAW;AAClB,SAAS,oBAAoB,oCAAoC;AACjE,SAAS,WAAW;AACpB,SAA6B,uBAAuB,oBAAoB;AACxE,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,+BAA+B;AACxC,SAAS,yBAAyB;AAO3B,MAAM,gBAAgB,CAAC,kBAAuC;AAInE,QAAM,mBAAmB,6BAA0D,eAAe,YAAY;AAC9G,mBAAiB,kBAAkB,qBAAqB;AAIxD,QAAM,eAAe,mBAAmB,gBAAgB;AAKxD,QAAM,cAAcA,OAAM,QAAQ,MAAM,cAAc,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAQlE,QAAM,EAAE,SAAS,WAAW,IAAI;AAChC,QAAM,eAAkD,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC;AAElG,QAAM,sCAAsC,wBAAwB;AAAA,IAClE,SAAS;AAAA,IACT;AAAA;AAAA;AAAA,EAGF,CAAC,EAAE,QAAQ;AACX,QAAM,eAAeA,OAAM;AAAA,IACzB,MAAO,MAAM,QAAQ,UAAU,IAAI,sCAAsC;AAAA,IACzE,CAAC,YAAY,mCAAmC;AAAA,EAClD;AAKA,QAAM,iBAAiBA,OAAM,QAAQ,MAAM,kBAAkB,YAAY,GAAG,CAAC,YAAY,CAAC;AAC1F,QAAM,uBAAuB,wBAAwB;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAOD,QAAM,WAAW,aAAa,QAAQ;AACtC,QAAM,wBAAwB,SAAS;AAAA,IACrC,CAAC,MAAM,OAAO,SAAS,KAAK,UAAU,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,MAAM;AAAA,EACzE;AACA,MAAI,sBAAsB,SAAS,GAAG;AACpC,UAAM,IAAI;AAAA,MACR;AAAA;AAAA,EAAoD,sBACjD,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,KAAK,CAAC;AAAA;AAAA;AAAA,IAChB;AAAA,EACF;AAEA,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL;AAAA,IACA,CAAC,kBAAkB,cAAc,aAAa,oBAAoB;AAAA,EACpE;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\nimport { useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport React from 'react';\nimport { uid } from 'uid';\nimport { DSMenuButtonPropTypes, menuSpecificDefaultProps, type DSMenuButtonT } from '../react-desc-prop-types.js';\nimport { getFocusableNodes } from '../utils/nodesTypeguardsAndGetters.js';\nimport { useOptionsArrayToDsTree } from '../utils/useOptionsArrayToDsTree.js';\nimport { useSplitInherithedProps } from './useSplitInherithedProps.js';\nimport { useValidateProps } from './useValidateProps.js';\nexport interface MenuButtonCTX extends ReturnType<typeof useSplitInherithedProps> {\n propsWithDefault: DSMenuButtonT.InternalProps;\n xstyledProps: ReturnType<typeof useGetXstyledProps>;\n instanceUid: string;\n}\n\nexport const useMenuButton = (propsFromUser: DSMenuButtonT.Props) => {\n // =============================================================================\n // MERGE WITH DEFAULT AND VALIDATE PROPS\n // =============================================================================\n const propsWithDefault = useMemoMergePropsWithDefault<DSMenuButtonT.InternalProps>(\n propsFromUser,\n menuSpecificDefaultProps,\n );\n useValidateProps(propsWithDefault, DSMenuButtonPropTypes);\n // =============================================================================\n // XSTYLED PROPS\n // =============================================================================\n const xstyledProps = useGetXstyledProps(propsWithDefault);\n // =============================================================================\n // AD HOC PER COMPONENT LOGIC\n // =============================================================================\n // custom code goes here, this is an example\n const instanceUid = React.useMemo(() => `menu-button${uid(5)}`, []);\n // =============================================================================\n // HELPERS HOOKS CONFIGS\n // =============================================================================\n\n // =============================================================================\n // React hooks can't be invoked conditionally...\n // =============================================================================\n\n // As per intended WIDGET API, user can provide options as an array of \"items\" or an array of \"nodes\"\n // we do the conversion from \"items\" to \"nodes\" here,\n // because the `parts` require the \"nodes\" extra properties to handle the logic and rendering\n const { options: appOptions } = propsWithDefault;\n const optionsArray: DSMenuButtonT.MenuItemInterface[] = Array.isArray(appOptions) ? appOptions : [];\n\n const treeRootNodeFromOptionsArrayIfArray = useOptionsArrayToDsTree({\n options: optionsArray,\n instanceUid,\n propsWithDefault,\n // this typecast is a safe type reconciliation, typescript checks they are compatible\n // this typecast let us avoid to have to do the ` | OtherCompatibleType` dance\n }).getRoot() as DSMenuButtonT.MenuNode;\n const treeRootNode = React.useMemo(\n () => (Array.isArray(appOptions) ? treeRootNodeFromOptionsArrayIfArray : appOptions),\n [appOptions, treeRootNodeFromOptionsArrayIfArray],\n );\n\n // =============================================================================\n // We calculate the focusableNodes from the treeRootNode.plainChildren via getFocusableNodes\n // =============================================================================\n const focusableNodes = React.useMemo(() => getFocusableNodes(treeRootNode), [treeRootNode]);\n const splitInherithedProps = useSplitInherithedProps({\n propsWithDefault,\n focusableNodes,\n treeRootNode,\n });\n\n return React.useMemo<MenuButtonCTX>(\n () => ({\n propsWithDefault,\n xstyledProps,\n instanceUid,\n ...splitInherithedProps,\n }),\n [propsWithDefault, xstyledProps, instanceUid, splitInherithedProps],\n );\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,oBAAoB,oCAAoC;AACjE,OAAOA,YAAW;AAClB,SAAS,WAAW;AACpB,SAAS,uBAAuB,gCAAoD;AACpF,SAAS,yBAAyB;AAClC,SAAS,+BAA+B;AACxC,SAAS,+BAA+B;AACxC,SAAS,wBAAwB;AAO1B,MAAM,gBAAgB,CAAC,kBAAuC;AAInE,QAAM,mBAAmB;AAAA,IACvB;AAAA,IACA;AAAA,EACF;AACA,mBAAiB,kBAAkB,qBAAqB;AAIxD,QAAM,eAAe,mBAAmB,gBAAgB;AAKxD,QAAM,cAAcA,OAAM,QAAQ,MAAM,cAAc,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAYlE,QAAM,EAAE,SAAS,WAAW,IAAI;AAChC,QAAM,eAAkD,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC;AAElG,QAAM,sCAAsC,wBAAwB;AAAA,IAClE,SAAS;AAAA,IACT;AAAA,IACA;AAAA;AAAA;AAAA,EAGF,CAAC,EAAE,QAAQ;AACX,QAAM,eAAeA,OAAM;AAAA,IACzB,MAAO,MAAM,QAAQ,UAAU,IAAI,sCAAsC;AAAA,IACzE,CAAC,YAAY,mCAAmC;AAAA,EAClD;AAKA,QAAM,iBAAiBA,OAAM,QAAQ,MAAM,kBAAkB,YAAY,GAAG,CAAC,YAAY,CAAC;AAC1F,QAAM,uBAAuB,wBAAwB;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL;AAAA,IACA,CAAC,kBAAkB,cAAc,aAAa,oBAAoB;AAAA,EACpE;AACF;",
6
6
  "names": ["React"]
7
7
  }
@@ -1,6 +1,28 @@
1
1
  import * as React from "react";
2
2
  import React2 from "react";
3
+ import { isSelectionableNode, isObjectAMenuNode } from "../utils/nodesTypeguardsAndGetters.js";
3
4
  import { resolveRef } from "../utils/resolveRef.js";
5
+ const convertSelectedOptionsToNodes = (selectedItems, treeRootNode) => {
6
+ const convertedSelection = [];
7
+ selectedItems.forEach((selectedItem) => {
8
+ const needsConversion = !isObjectAMenuNode(selectedItem);
9
+ if (!needsConversion) {
10
+ convertedSelection.push(selectedItem);
11
+ return;
12
+ }
13
+ const { dsId } = selectedItem;
14
+ const selectedNode = treeRootNode.findNode((node) => node.dsId === dsId);
15
+ if (!selectedNode) {
16
+ throw new Error(`Selected option with dsId ${dsId} not found in the menu tree.`);
17
+ }
18
+ if (!isSelectionableNode(selectedNode)) {
19
+ console.warn(`Selected option with dsId ${dsId} is not a selectionable node and will be ignored.`);
20
+ return;
21
+ }
22
+ convertedSelection.push(selectedNode);
23
+ });
24
+ return convertedSelection;
25
+ };
4
26
  const useSplitInherithedProps = ({
5
27
  propsWithDefault,
6
28
  focusableNodes,
@@ -15,7 +37,6 @@ const useSplitInherithedProps = ({
15
37
  onSubmenuToggle,
16
38
  isLoading,
17
39
  isSkeleton,
18
- selectedOptions,
19
40
  ItemRenderer,
20
41
  innerRef,
21
42
  selectedItems,
@@ -24,6 +45,8 @@ const useSplitInherithedProps = ({
24
45
  onActivateItem,
25
46
  onOpen,
26
47
  onClose,
48
+ maxHeight,
49
+ // this is not shared with anything at all, it's instead a property of the "root" node, each node (with a submenu) can have its own maxHeight
27
50
  ...buttonInheritedProps
28
51
  } = propsWithDefault;
29
52
  const innerRefSnatchingNode = React2.useCallback(
@@ -35,6 +58,12 @@ const useSplitInherithedProps = ({
35
58
  },
36
59
  [innerRef]
37
60
  );
61
+ const selectedNodesMap = React2.useMemo(() => {
62
+ if (selectedItems) {
63
+ return convertSelectedOptionsToNodes(selectedItems, treeRootNode);
64
+ }
65
+ return [];
66
+ }, [selectedItems, treeRootNode]);
38
67
  const menuSpecificProps = React2.useMemo(
39
68
  () => ({
40
69
  options,
@@ -44,26 +73,15 @@ const useSplitInherithedProps = ({
44
73
  onSubmenuToggle,
45
74
  isLoading,
46
75
  isSkeleton,
47
- selectedOptions,
48
76
  ItemRenderer
49
77
  }),
50
- [
51
- options,
52
- onClickOutside,
53
- onOptionClick,
54
- openedSubmenus,
55
- onSubmenuToggle,
56
- isLoading,
57
- isSkeleton,
58
- selectedOptions,
59
- ItemRenderer
60
- ]
78
+ [options, onClickOutside, onOptionClick, openedSubmenus, onSubmenuToggle, isLoading, isSkeleton, ItemRenderer]
61
79
  );
62
80
  return React2.useMemo(
63
81
  () => ({
64
82
  menuBehaviouralLayerProps: {
65
83
  buttonDOMNodeRef,
66
- selectedItems,
84
+ selectedNodes: selectedNodesMap,
67
85
  focusableNodes,
68
86
  optionsTree: treeRootNode,
69
87
  onDisplayedSubmenuChange,
@@ -79,7 +97,7 @@ const useSplitInherithedProps = ({
79
97
  }
80
98
  }),
81
99
  [
82
- selectedItems,
100
+ selectedNodesMap,
83
101
  focusableNodes,
84
102
  treeRootNode,
85
103
  onDisplayedSubmenuChange,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/config/useSplitInherithedProps.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* *******************************************************\n * From official source definition\n *\n * Menu Button Pattern\n * About This Pattern:\n * A menu button is a button\n * that opens a menu\n * (as described in the Menu and Menubar Pattern).\n *\n * Since the menu button is a button, it inherits all the props from the Button component.\n * Since the menu button MUST have a menu, it also have a set of specific props used to handle the menu.\n * Because we build with atomic composition in mind, the \"logic layer\" is separated and has a yet another set of props.\n *\n * This is effectively an OOP \"extension\" of the Button component,\n * so it has the same props,\n * BUT also a few more to handle the specific behavior of the menu.\n *\n * this hooks is meant to take all the DSMenuButtonT.InternalProps with default already merged\n * and spit out menuSpecificProps & buttonInheritedProps\n ******************************************************* */\nimport {} from '@elliemae/ds-system';\nimport React from 'react';\nimport { type DSMenuButtonT } from '../react-desc-prop-types.js';\nimport { resolveRef } from '../utils/resolveRef.js';\n\ntype UseSplitInherithedPropsConfig = {\n propsWithDefault: DSMenuButtonT.InternalProps;\n focusableNodes: DSMenuButtonT.PseudoFocusableMenuNodes[];\n treeRootNode: DSMenuButtonT.MenuNode;\n};\nexport const useSplitInherithedProps = ({\n propsWithDefault,\n focusableNodes,\n treeRootNode,\n}: UseSplitInherithedPropsConfig) => {\n const buttonDOMNodeRef = React.useRef<HTMLElement | null>(null);\n // when this component has been wrote\n // =============================================================================\n // MENU BUTTON props are:\n // Required\n // optionsTree, menuSpecificProps\n // Default (we already have a value because props for this hook are already merged with default props)\n // onClickOutside, onOptionClick, openedSubmenus, onSubmenuToggle, isLoading, isSkeleton, selectedOptions\n // Optional (may or may not be present)\n // ItemRenderer\n // =============================================================================\n // Props for the DSMenuBehaviouralContextProvider\n // =============================================================================\n // Required\n // selectedItems, onItemSelected, onActivateItem,\n // Optional\n // onDisplayedSubmenuChange, onOpen, onClose\n\n const {\n options,\n onClickOutside,\n onOptionClick,\n openedSubmenus,\n onSubmenuToggle,\n isLoading,\n isSkeleton,\n selectedOptions,\n ItemRenderer,\n innerRef,\n selectedItems,\n onDisplayedSubmenuChange,\n onItemSelected,\n onActivateItem,\n onOpen,\n onClose,\n ...buttonInheritedProps\n } = propsWithDefault;\n\n // the button is allowed to receive innerRef, but we also need to invoke the setButtonDOMNode to store the button node\n // so we create a functional ref to do both\n const innerRefSnatchingNode: Required<DSMenuButtonT.Props>['innerRef'] = React.useCallback(\n (node: HTMLButtonElement) => {\n buttonDOMNodeRef.current = node;\n if (innerRef) {\n resolveRef(innerRef, node);\n }\n },\n [innerRef],\n );\n const menuSpecificProps = React.useMemo(\n () => ({\n options,\n onClickOutside,\n onOptionClick,\n openedSubmenus,\n onSubmenuToggle,\n isLoading,\n isSkeleton,\n selectedOptions,\n ItemRenderer,\n }),\n [\n options,\n onClickOutside,\n onOptionClick,\n openedSubmenus,\n onSubmenuToggle,\n isLoading,\n isSkeleton,\n selectedOptions,\n ItemRenderer,\n ],\n );\n\n return React.useMemo(\n () => ({\n menuBehaviouralLayerProps: {\n buttonDOMNodeRef,\n selectedItems,\n focusableNodes,\n optionsTree: treeRootNode,\n onDisplayedSubmenuChange,\n onItemSelected,\n onActivateItem,\n onOpen,\n onClose,\n },\n opinionatedButtonProps: {\n ...(buttonInheritedProps as DSMenuButtonT.ButtonInheritedProps),\n innerRef: innerRefSnatchingNode,\n menuSpecificProps,\n },\n }),\n [\n selectedItems,\n focusableNodes,\n treeRootNode,\n onDisplayedSubmenuChange,\n onItemSelected,\n onActivateItem,\n onOpen,\n onClose,\n buttonInheritedProps,\n innerRefSnatchingNode,\n menuSpecificProps,\n ],\n );\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACqBvB,OAAOA,YAAW;AAElB,SAAS,kBAAkB;AAOpB,MAAM,0BAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,MAAqC;AACnC,QAAM,mBAAmBA,OAAM,OAA2B,IAAI;AAkB9D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAIJ,QAAM,wBAAmEA,OAAM;AAAA,IAC7E,CAAC,SAA4B;AAC3B,uBAAiB,UAAU;AAC3B,UAAI,UAAU;AACZ,mBAAW,UAAU,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,oBAAoBA,OAAM;AAAA,IAC9B,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL,2BAA2B;AAAA,QACzB;AAAA,QACA;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,GAAI;AAAA,QACJ,UAAU;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* *******************************************************\n * From official source definition\n *\n * Menu Button Pattern\n * About This Pattern:\n * A menu button is a button\n * that opens a menu\n * (as described in the Menu and Menubar Pattern).\n *\n * Since the menu button is a button, it inherits all the props from the Button component.\n * Since the menu button MUST have a menu, it also have a set of specific props used to handle the menu.\n * Because we build with atomic composition in mind, the \"logic layer\" is separated and has a yet another set of props.\n *\n * This is effectively an OOP \"extension\" of the Button component,\n * so it has the same props,\n * BUT also a few more to handle the specific behavior of the menu.\n *\n * this hooks is meant to take all the DSMenuButtonT.InternalProps with default already merged\n * and spit out menuSpecificProps & buttonInheritedProps\n ******************************************************* */\nimport {} from '@elliemae/ds-system';\nimport React from 'react';\nimport { type DSMenuButtonT } from '../react-desc-prop-types.js';\nimport { isSelectionableNode, isObjectAMenuNode } from '../utils/nodesTypeguardsAndGetters.js';\nimport { resolveRef } from '../utils/resolveRef.js';\n\n/**\n * Converts selected options to a map of selected nodes.\n * @param {DSMenuButtonT.SelectionableMenuNodes[]} selectedItems - The selected options, as provided by the user (either items or nodes).\n * @param {DSMenuButtonT.MenuNode} treeRootNode - The root node of the menu tree.\n * @returns {DSMenuButtonT.SelectionableMenuNodes[]} - The map of selected nodes.\n * @throws Will throw an error if a selected option is not found in the tree.\n */\nconst convertSelectedOptionsToNodes = (\n selectedItems: DSMenuButtonT.SelectionableMenuNodes[] | DSMenuButtonT.SelectionableMenuItemInterface[],\n treeRootNode: DSMenuButtonT.MenuNode,\n): DSMenuButtonT.SelectionableMenuNodes[] => {\n const convertedSelection: DSMenuButtonT.SelectionableMenuNodes[] = [];\n\n selectedItems.forEach((selectedItem) => {\n const needsConversion = !isObjectAMenuNode(selectedItem);\n if (!needsConversion) {\n convertedSelection.push(selectedItem);\n return;\n }\n\n const { dsId } = selectedItem;\n const selectedNode = treeRootNode.findNode((node: DSMenuButtonT.MenuNode) => node.dsId === dsId);\n if (!selectedNode) {\n throw new Error(`Selected option with dsId ${dsId} not found in the menu tree.`);\n }\n if (!isSelectionableNode(selectedNode)) {\n // eslint-disable-next-line no-console\n console.warn(`Selected option with dsId ${dsId} is not a selectionable node and will be ignored.`);\n return;\n }\n convertedSelection.push(selectedNode);\n });\n\n return convertedSelection;\n};\n\ntype UseSplitInherithedPropsConfig = {\n propsWithDefault: DSMenuButtonT.InternalProps;\n focusableNodes: DSMenuButtonT.PseudoFocusableMenuNodes[];\n treeRootNode: DSMenuButtonT.MenuNode;\n};\nexport const useSplitInherithedProps = ({\n propsWithDefault,\n focusableNodes,\n treeRootNode,\n}: UseSplitInherithedPropsConfig) => {\n const buttonDOMNodeRef = React.useRef<HTMLElement | null>(null);\n // when this component has been wrote\n // =============================================================================\n // MENU BUTTON props are:\n // Required\n // optionsTree, menuSpecificProps\n // Default (we already have a value because props for this hook are already merged with default props)\n // onClickOutside, onOptionClick, openedSubmenus, onSubmenuToggle, isLoading, isSkeleton, selectedOptions\n // Optional (may or may not be present)\n // ItemRenderer\n // =============================================================================\n // Props for the DSMenuBehaviouralContextProvider\n // =============================================================================\n // Required\n // selectedItems, onItemSelected, onActivateItem,\n // Optional\n // onDisplayedSubmenuChange, onOpen, onClose\n\n const {\n options,\n onClickOutside,\n onOptionClick,\n openedSubmenus,\n onSubmenuToggle,\n isLoading,\n isSkeleton,\n ItemRenderer,\n innerRef,\n selectedItems,\n onDisplayedSubmenuChange,\n onItemSelected,\n onActivateItem,\n onOpen,\n onClose,\n maxHeight, // this is not shared with anything at all, it's instead a property of the \"root\" node, each node (with a submenu) can have its own maxHeight\n ...buttonInheritedProps\n } = propsWithDefault;\n\n // the button is allowed to receive innerRef, but we also need to invoke the setButtonDOMNode to store the button node\n // so we create a functional ref to do both\n const innerRefSnatchingNode: Required<DSMenuButtonT.Props>['innerRef'] = React.useCallback(\n (node: HTMLButtonElement) => {\n buttonDOMNodeRef.current = node;\n if (innerRef) {\n resolveRef(innerRef, node);\n }\n },\n [innerRef],\n );\n // in the WIDGET API, the user can provide selectedItems as an array of \"items\" or an array of \"nodes\"\n // the component needs the \"nodes\" to handle the logic and rendering & accessibility\n // if the user provides \"items\", we convert them to \"nodes\" here\n const selectedNodesMap = React.useMemo(() => {\n if (selectedItems) {\n return convertSelectedOptionsToNodes(selectedItems, treeRootNode);\n }\n return [];\n }, [selectedItems, treeRootNode]);\n\n const menuSpecificProps = React.useMemo(\n () => ({\n options,\n onClickOutside,\n onOptionClick,\n openedSubmenus,\n onSubmenuToggle,\n isLoading,\n isSkeleton,\n ItemRenderer,\n }),\n [options, onClickOutside, onOptionClick, openedSubmenus, onSubmenuToggle, isLoading, isSkeleton, ItemRenderer],\n );\n\n return React.useMemo(\n () => ({\n menuBehaviouralLayerProps: {\n buttonDOMNodeRef,\n selectedNodes: selectedNodesMap,\n focusableNodes,\n optionsTree: treeRootNode,\n onDisplayedSubmenuChange,\n onItemSelected,\n onActivateItem,\n onOpen,\n onClose,\n },\n opinionatedButtonProps: {\n ...(buttonInheritedProps as DSMenuButtonT.ButtonInheritedProps),\n innerRef: innerRefSnatchingNode,\n menuSpecificProps,\n },\n }),\n [\n selectedNodesMap,\n focusableNodes,\n treeRootNode,\n onDisplayedSubmenuChange,\n onItemSelected,\n onActivateItem,\n onOpen,\n onClose,\n buttonInheritedProps,\n innerRefSnatchingNode,\n menuSpecificProps,\n ],\n );\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACqBvB,OAAOA,YAAW;AAElB,SAAS,qBAAqB,yBAAyB;AACvD,SAAS,kBAAkB;AAS3B,MAAM,gCAAgC,CACpC,eACA,iBAC2C;AAC3C,QAAM,qBAA6D,CAAC;AAEpE,gBAAc,QAAQ,CAAC,iBAAiB;AACtC,UAAM,kBAAkB,CAAC,kBAAkB,YAAY;AACvD,QAAI,CAAC,iBAAiB;AACpB,yBAAmB,KAAK,YAAY;AACpC;AAAA,IACF;AAEA,UAAM,EAAE,KAAK,IAAI;AACjB,UAAM,eAAe,aAAa,SAAS,CAAC,SAAiC,KAAK,SAAS,IAAI;AAC/F,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,6BAA6B,IAAI,8BAA8B;AAAA,IACjF;AACA,QAAI,CAAC,oBAAoB,YAAY,GAAG;AAEtC,cAAQ,KAAK,6BAA6B,IAAI,mDAAmD;AACjG;AAAA,IACF;AACA,uBAAmB,KAAK,YAAY;AAAA,EACtC,CAAC;AAED,SAAO;AACT;AAOO,MAAM,0BAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,MAAqC;AACnC,QAAM,mBAAmBA,OAAM,OAA2B,IAAI;AAkB9D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAIJ,QAAM,wBAAmEA,OAAM;AAAA,IAC7E,CAAC,SAA4B;AAC3B,uBAAiB,UAAU;AAC3B,UAAI,UAAU;AACZ,mBAAW,UAAU,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAIA,QAAM,mBAAmBA,OAAM,QAAQ,MAAM;AAC3C,QAAI,eAAe;AACjB,aAAO,8BAA8B,eAAe,YAAY;AAAA,IAClE;AACA,WAAO,CAAC;AAAA,EACV,GAAG,CAAC,eAAe,YAAY,CAAC;AAEhC,QAAM,oBAAoBA,OAAM;AAAA,IAC9B,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,SAAS,gBAAgB,eAAe,gBAAgB,iBAAiB,WAAW,YAAY,YAAY;AAAA,EAC/G;AAEA,SAAOA,OAAM;AAAA,IACX,OAAO;AAAA,MACL,2BAA2B;AAAA,QACzB;AAAA,QACA,eAAe;AAAA,QACf;AAAA,QACA,aAAa;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,GAAI;AAAA,QACJ,UAAU;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": ["React"]
7
7
  }
package/dist/esm/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { DSMenuButton, DSMenuButtonWithSchema } from "./DSMenuButton.js";
3
+ import { DSMenuButtonPropTypes } from "./react-desc-prop-types.js";
3
4
  import { MENU_BUTTON_SLOTS, MENU_BUTTON_DATA_TESTID, MENU_ITEMS_TYPES } from "./constants/index.js";
4
5
  import { isFocusableNode } from "./utils/nodesTypeguardsAndGetters.js";
5
6
  import { useOptionsArrayToDsTree } from "./utils/useOptionsArrayToDsTree.js";
6
7
  export {
7
8
  DSMenuButton,
9
+ DSMenuButtonPropTypes,
8
10
  DSMenuButtonWithSchema,
9
11
  MENU_BUTTON_DATA_TESTID,
10
12
  MENU_BUTTON_SLOTS,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/index.ts"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "// this is a workaround to typescript error TS2742\n// https://github.com/microsoft/TypeScript/issues/47663\nimport type {} from '@xstyled/system';\nexport { DSMenuButton, DSMenuButtonWithSchema } from './DSMenuButton.js';\nexport { type DSMenuButtonT } from './react-desc-prop-types.js';\nexport { MENU_BUTTON_SLOTS, MENU_BUTTON_DATA_TESTID, MENU_ITEMS_TYPES } from './constants/index.js';\nexport { isFocusableNode } from './utils/nodesTypeguardsAndGetters.js';\nexport { useOptionsArrayToDsTree } from './utils/useOptionsArrayToDsTree.js';\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACGvB,SAAS,cAAc,8BAA8B;AAErD,SAAS,mBAAmB,yBAAyB,wBAAwB;AAC7E,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "// this is a workaround to typescript error TS2742\n// https://github.com/microsoft/TypeScript/issues/47663\nimport type {} from '@xstyled/system';\nexport { DSMenuButton, DSMenuButtonWithSchema } from './DSMenuButton.js';\nexport { type DSMenuButtonT, DSMenuButtonPropTypes } from './react-desc-prop-types.js';\nexport { MENU_BUTTON_SLOTS, MENU_BUTTON_DATA_TESTID, MENU_ITEMS_TYPES } from './constants/index.js';\nexport { isFocusableNode } from './utils/nodesTypeguardsAndGetters.js';\nexport { useOptionsArrayToDsTree } from './utils/useOptionsArrayToDsTree.js';\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACGvB,SAAS,cAAc,8BAA8B;AACrD,SAA6B,6BAA6B;AAC1D,SAAS,mBAAmB,yBAAyB,wBAAwB;AAC7E,SAAS,uBAAuB;AAChC,SAAS,+BAA+B;",
6
6
  "names": []
7
7
  }
@@ -5,28 +5,36 @@ import { Grid } from "@elliemae/ds-grid";
5
5
  import { describe } from "@elliemae/ds-props-helpers";
6
6
  import { styled, xStyledCommonProps } from "@elliemae/ds-system";
7
7
  import React2 from "react";
8
+ import { menuSpecificDefaultProps } from "../../react-desc-prop-types.js";
8
9
  import { MenuBehaviouralContextProviderContext } from "../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js";
9
10
  import { DSMenuItemRendererFactory } from "../DSMenuItemRendererFactory/index.js";
10
11
  import { useFlyoutMenu } from "./config/useFlyoutMenu.js";
11
12
  import { DSFlyoutMenuName, FLYOUT_MENU_SLOTS } from "./constants/index.js";
12
13
  import { DSFlyoutMenuPropTypesSchema } from "./react-desc-prop-types.js";
13
14
  import { isRootNode } from "../../utils/nodesTypeguardsAndGetters.js";
14
- const StyledWrapper = styled(Grid, { name: DSFlyoutMenuName, slot: FLYOUT_MENU_SLOTS.ROOT })`
15
- min-width: 150px;
15
+ const StyledWrapper = styled(Grid, {
16
+ name: DSFlyoutMenuName,
17
+ slot: FLYOUT_MENU_SLOTS.ROOT
18
+ })`
19
+ min-width: 1px;
16
20
  background-color: white;
17
21
  padding: ${({ theme }) => theme.space.xxxs} 0;
18
22
  ${xStyledCommonProps}
19
23
  `;
20
24
  const StyledMenu = styled("div", { name: DSFlyoutMenuName, slot: FLYOUT_MENU_SLOTS.LIST_WRAPPER })`
25
+ max-height: ${({ $maxHeight }) => typeof $maxHeight === "number" ? `${$maxHeight}px` : $maxHeight};
26
+ overflow-y: auto;
21
27
  padding: 0;
22
28
  margin: 0;
23
29
  `;
30
+ const NoComponentPlaceholder = () => null;
24
31
  const DSFlyoutMenu = (props) => {
25
32
  const { propsWithDefault, xstyledProps } = useFlyoutMenu(props);
26
33
  const {
27
34
  instanceUid,
28
35
  globalEventsHelpers: { mainMenuRef, handleSubmenusRefChange }
29
36
  } = React2.useContext(MenuBehaviouralContextProviderContext);
37
+ const [didAnimationEnd, setDidAnimationEnd] = React2.useState(false);
30
38
  const { setFloatingRef, floatingStyles, floatingContext, ItemRenderer, isMenuOpen, itemNode } = propsWithDefault;
31
39
  const handleRefChange = React2.useCallback(
32
40
  (node) => {
@@ -38,6 +46,12 @@ const DSFlyoutMenu = (props) => {
38
46
  },
39
47
  [handleSubmenusRefChange, itemNode, mainMenuRef]
40
48
  );
49
+ const handleAnimationStartTrigger = React2.useCallback(() => {
50
+ setDidAnimationEnd(false);
51
+ }, []);
52
+ const handleAnimationEnd = React2.useCallback(() => {
53
+ setDidAnimationEnd(true);
54
+ }, []);
41
55
  return /* @__PURE__ */ jsx(
42
56
  FloatingWrapper,
43
57
  {
@@ -45,15 +59,25 @@ const DSFlyoutMenu = (props) => {
45
59
  floatingStyles,
46
60
  isOpen: isMenuOpen,
47
61
  context: floatingContext,
48
- children: /* @__PURE__ */ jsx(StyledWrapper, { getOwnerProps: () => propsWithDefault, getOwnerPropsArguments: () => ({}), ...xstyledProps, children: /* @__PURE__ */ jsx(StyledMenu, { role: "menu", innerRef: handleRefChange, children: itemNode.children.map((optionNode) => /* @__PURE__ */ jsx(
49
- DSMenuItemRendererFactory,
62
+ onAnimationStartTriggered: handleAnimationStartTrigger,
63
+ onAnimationEnd: handleAnimationEnd,
64
+ children: /* @__PURE__ */ jsx(StyledWrapper, { getOwnerProps: () => propsWithDefault, getOwnerPropsArguments: () => ({}), ...xstyledProps, children: /* @__PURE__ */ jsx(
65
+ StyledMenu,
50
66
  {
51
- itemNode: optionNode,
52
- ItemRenderer,
53
- FlyoutMenuCircularDepInject: DSFlyoutMenu
54
- },
55
- `flyout-menu-item-${optionNode.dsId}-${instanceUid}`
56
- )) }) })
67
+ role: "menu",
68
+ innerRef: handleRefChange,
69
+ $maxHeight: itemNode.plainItem.maxHeight ?? menuSpecificDefaultProps.maxHeight,
70
+ children: itemNode.children.map((optionNode) => /* @__PURE__ */ jsx(
71
+ DSMenuItemRendererFactory,
72
+ {
73
+ itemNode: optionNode,
74
+ ItemRenderer,
75
+ FlyoutMenuCircularDepInject: didAnimationEnd ? DSFlyoutMenu : NoComponentPlaceholder
76
+ },
77
+ `flyout-menu-item-${optionNode.dsId}-${instanceUid}`
78
+ ))
79
+ }
80
+ ) })
57
81
  }
58
82
  );
59
83
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/DSFlyoutMenu/DSFlyoutMenu.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { FloatingWrapper } from '@elliemae/ds-floating-context';\nimport { Grid } from '@elliemae/ds-grid';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport React from 'react';\nimport type { DSMenuButtonT } from '../../react-desc-prop-types.js';\nimport { MenuBehaviouralContextProviderContext } from '../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js';\nimport { DSMenuItemRendererFactory } from '../DSMenuItemRendererFactory/index.js';\nimport { useFlyoutMenu } from './config/useFlyoutMenu.js';\nimport { DSFlyoutMenuName, FLYOUT_MENU_SLOTS } from './constants/index.js';\nimport { DSFlyoutMenuPropTypesSchema, type DSFlyoutMenuT } from './react-desc-prop-types.js';\nimport { isRootNode } from '../../utils/nodesTypeguardsAndGetters.js';\n\nconst StyledWrapper = styled(Grid, { name: DSFlyoutMenuName, slot: FLYOUT_MENU_SLOTS.ROOT })`\n min-width: 150px;\n background-color: white;\n padding: ${({ theme }) => theme.space.xxxs} 0;\n ${xStyledCommonProps}\n`;\n\nconst StyledMenu = styled('div', { name: DSFlyoutMenuName, slot: FLYOUT_MENU_SLOTS.LIST_WRAPPER })`\n padding: 0;\n margin: 0;\n`;\n\nconst DSFlyoutMenu: React.ComponentType<DSFlyoutMenuT.Props> = (props) => {\n const { propsWithDefault, xstyledProps } = useFlyoutMenu(props);\n const {\n instanceUid,\n globalEventsHelpers: { mainMenuRef, handleSubmenusRefChange },\n } = React.useContext(MenuBehaviouralContextProviderContext);\n\n const { setFloatingRef, floatingStyles, floatingContext, ItemRenderer, isMenuOpen, itemNode } = propsWithDefault;\n\n const handleRefChange = React.useCallback(\n (node: HTMLDivElement) => {\n if (isRootNode(itemNode)) {\n mainMenuRef.current = node;\n return;\n }\n handleSubmenusRefChange(node, itemNode.dsId);\n },\n [handleSubmenusRefChange, itemNode, mainMenuRef],\n );\n\n return (\n <FloatingWrapper\n innerRef={setFloatingRef}\n floatingStyles={floatingStyles}\n isOpen={isMenuOpen}\n context={floatingContext}\n >\n <StyledWrapper getOwnerProps={() => propsWithDefault} getOwnerPropsArguments={() => ({})} {...xstyledProps}>\n <StyledMenu role=\"menu\" innerRef={handleRefChange}>\n {itemNode.children.map((optionNode) => (\n <DSMenuItemRendererFactory\n key={`flyout-menu-item-${optionNode.dsId}-${instanceUid}`}\n // DSTree doesn't support children having a polymorphic type, so we need to cast it manually\n itemNode={optionNode as DSMenuButtonT.MenuNode}\n ItemRenderer={ItemRenderer}\n // we are passing the DSFlyoutMenu component as a prop to the DSMenuItemRendererFactory\n // this solves the circular-ish dependency\n FlyoutMenuCircularDepInject={DSFlyoutMenu}\n />\n ))}\n </StyledMenu>\n </StyledWrapper>\n </FloatingWrapper>\n );\n};\n\nDSFlyoutMenu.displayName = DSFlyoutMenuName;\nconst DSFlyoutMenuWithSchema = describe(DSFlyoutMenu);\nDSFlyoutMenuWithSchema.propTypes = DSFlyoutMenuPropTypesSchema;\n\nexport { DSFlyoutMenu, DSFlyoutMenuWithSchema };\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACuDX;AAvDZ,SAAS,uBAAuB;AAChC,SAAS,YAAY;AACrB,SAAS,gBAAgB;AACzB,SAAS,QAAQ,0BAA0B;AAC3C,OAAOA,YAAW;AAElB,SAAS,6CAA6C;AACtD,SAAS,iCAAiC;AAC1C,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,yBAAyB;AACpD,SAAS,mCAAuD;AAChE,SAAS,kBAAkB;AAE3B,MAAM,gBAAgB,OAAO,MAAM,EAAE,MAAM,kBAAkB,MAAM,kBAAkB,KAAK,CAAC;AAAA;AAAA;AAAA,aAG9E,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,IAAI;AAAA,IACxC,kBAAkB;AAAA;AAGtB,MAAM,aAAa,OAAO,OAAO,EAAE,MAAM,kBAAkB,MAAM,kBAAkB,aAAa,CAAC;AAAA;AAAA;AAAA;AAKjG,MAAM,eAAyD,CAAC,UAAU;AACxE,QAAM,EAAE,kBAAkB,aAAa,IAAI,cAAc,KAAK;AAC9D,QAAM;AAAA,IACJ;AAAA,IACA,qBAAqB,EAAE,aAAa,wBAAwB;AAAA,EAC9D,IAAIA,OAAM,WAAW,qCAAqC;AAE1D,QAAM,EAAE,gBAAgB,gBAAgB,iBAAiB,cAAc,YAAY,SAAS,IAAI;AAEhG,QAAM,kBAAkBA,OAAM;AAAA,IAC5B,CAAC,SAAyB;AACxB,UAAI,WAAW,QAAQ,GAAG;AACxB,oBAAY,UAAU;AACtB;AAAA,MACF;AACA,8BAAwB,MAAM,SAAS,IAAI;AAAA,IAC7C;AAAA,IACA,CAAC,yBAAyB,UAAU,WAAW;AAAA,EACjD;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,MAET,8BAAC,iBAAc,eAAe,MAAM,kBAAkB,wBAAwB,OAAO,CAAC,IAAK,GAAG,cAC5F,8BAAC,cAAW,MAAK,QAAO,UAAU,iBAC/B,mBAAS,SAAS,IAAI,CAAC,eACtB;AAAA,QAAC;AAAA;AAAA,UAGC,UAAU;AAAA,UACV;AAAA,UAGA,6BAA6B;AAAA;AAAA,QANxB,oBAAoB,WAAW,IAAI,IAAI,WAAW;AAAA,MAOzD,CACD,GACH,GACF;AAAA;AAAA,EACF;AAEJ;AAEA,aAAa,cAAc;AAC3B,MAAM,yBAAyB,SAAS,YAAY;AACpD,uBAAuB,YAAY;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { FloatingWrapper } from '@elliemae/ds-floating-context';\nimport { Grid } from '@elliemae/ds-grid';\nimport { describe } from '@elliemae/ds-props-helpers';\nimport { styled, xStyledCommonProps } from '@elliemae/ds-system';\nimport React from 'react';\nimport { type DSMenuButtonT, menuSpecificDefaultProps } from '../../react-desc-prop-types.js';\nimport { MenuBehaviouralContextProviderContext } from '../DSMenuBehaviouralContextProvider/MenuBehaviouralContextProviderCTX.js';\nimport { DSMenuItemRendererFactory } from '../DSMenuItemRendererFactory/index.js';\nimport { useFlyoutMenu } from './config/useFlyoutMenu.js';\nimport { DSFlyoutMenuName, FLYOUT_MENU_SLOTS } from './constants/index.js';\nimport { DSFlyoutMenuPropTypesSchema, type DSFlyoutMenuT } from './react-desc-prop-types.js';\nimport { isRootNode } from '../../utils/nodesTypeguardsAndGetters.js';\n\nconst StyledWrapper = styled(Grid, {\n name: DSFlyoutMenuName,\n slot: FLYOUT_MENU_SLOTS.ROOT,\n})`\n min-width: 1px;\n background-color: white;\n padding: ${({ theme }) => theme.space.xxxs} 0;\n ${xStyledCommonProps}\n`;\n\nconst StyledMenu = styled('div', { name: DSFlyoutMenuName, slot: FLYOUT_MENU_SLOTS.LIST_WRAPPER })<{\n $maxHeight: string | number;\n}>`\n max-height: ${({ $maxHeight }) => (typeof $maxHeight === 'number' ? `${$maxHeight}px` : $maxHeight)};\n overflow-y: auto;\n padding: 0;\n margin: 0;\n`;\n\nconst NoComponentPlaceholder = () => null;\n\nconst DSFlyoutMenu: React.ComponentType<DSFlyoutMenuT.Props> = (props) => {\n const { propsWithDefault, xstyledProps } = useFlyoutMenu(props);\n const {\n instanceUid,\n globalEventsHelpers: { mainMenuRef, handleSubmenusRefChange },\n } = React.useContext(MenuBehaviouralContextProviderContext);\n const [didAnimationEnd, setDidAnimationEnd] = React.useState(false);\n\n const { setFloatingRef, floatingStyles, floatingContext, ItemRenderer, isMenuOpen, itemNode } = propsWithDefault;\n\n const handleRefChange = React.useCallback(\n (node: HTMLDivElement) => {\n if (isRootNode(itemNode)) {\n mainMenuRef.current = node;\n return;\n }\n handleSubmenusRefChange(node, itemNode.dsId);\n },\n [handleSubmenusRefChange, itemNode, mainMenuRef],\n );\n\n const handleAnimationStartTrigger = React.useCallback(() => {\n setDidAnimationEnd(false);\n }, []);\n const handleAnimationEnd = React.useCallback(() => {\n setDidAnimationEnd(true);\n }, []);\n\n return (\n <FloatingWrapper\n innerRef={setFloatingRef}\n floatingStyles={floatingStyles}\n isOpen={isMenuOpen}\n context={floatingContext}\n onAnimationStartTriggered={handleAnimationStartTrigger}\n onAnimationEnd={handleAnimationEnd}\n >\n <StyledWrapper getOwnerProps={() => propsWithDefault} getOwnerPropsArguments={() => ({})} {...xstyledProps}>\n <StyledMenu\n role=\"menu\"\n innerRef={handleRefChange}\n $maxHeight={itemNode.plainItem.maxHeight ?? menuSpecificDefaultProps.maxHeight}\n >\n {itemNode.children.map((optionNode) => (\n <DSMenuItemRendererFactory\n key={`flyout-menu-item-${optionNode.dsId}-${instanceUid}`}\n // DSTree doesn't support children having a polymorphic type, so we need to cast it manually\n itemNode={optionNode as DSMenuButtonT.MenuNode}\n ItemRenderer={ItemRenderer}\n // we are passing the DSFlyoutMenu component as a prop to the DSMenuItemRendererFactory\n // this solves the circular-ish dependency\n // the conditional `didAnimationEnd` solves the visual artefact of nested FloatingWrapper wrongly positioned due to animation\n FlyoutMenuCircularDepInject={didAnimationEnd ? DSFlyoutMenu : NoComponentPlaceholder}\n />\n ))}\n </StyledMenu>\n </StyledWrapper>\n </FloatingWrapper>\n );\n};\n\nDSFlyoutMenu.displayName = DSFlyoutMenuName;\nconst DSFlyoutMenuWithSchema = describe(DSFlyoutMenu);\nDSFlyoutMenuWithSchema.propTypes = DSFlyoutMenuPropTypesSchema;\n\nexport { DSFlyoutMenu, DSFlyoutMenuWithSchema };\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AC8EX;AA9EZ,SAAS,uBAAuB;AAChC,SAAS,YAAY;AACrB,SAAS,gBAAgB;AACzB,SAAS,QAAQ,0BAA0B;AAC3C,OAAOA,YAAW;AAClB,SAA6B,gCAAgC;AAC7D,SAAS,6CAA6C;AACtD,SAAS,iCAAiC;AAC1C,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,yBAAyB;AACpD,SAAS,mCAAuD;AAChE,SAAS,kBAAkB;AAE3B,MAAM,gBAAgB,OAAO,MAAM;AAAA,EACjC,MAAM;AAAA,EACN,MAAM,kBAAkB;AAC1B,CAAC;AAAA;AAAA;AAAA,aAGY,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,IAAI;AAAA,IACxC,kBAAkB;AAAA;AAGtB,MAAM,aAAa,OAAO,OAAO,EAAE,MAAM,kBAAkB,MAAM,kBAAkB,aAAa,CAAC;AAAA,gBAGjF,CAAC,EAAE,WAAW,MAAO,OAAO,eAAe,WAAW,GAAG,UAAU,OAAO,UAAW;AAAA;AAAA;AAAA;AAAA;AAMrG,MAAM,yBAAyB,MAAM;AAErC,MAAM,eAAyD,CAAC,UAAU;AACxE,QAAM,EAAE,kBAAkB,aAAa,IAAI,cAAc,KAAK;AAC9D,QAAM;AAAA,IACJ;AAAA,IACA,qBAAqB,EAAE,aAAa,wBAAwB;AAAA,EAC9D,IAAIA,OAAM,WAAW,qCAAqC;AAC1D,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,OAAM,SAAS,KAAK;AAElE,QAAM,EAAE,gBAAgB,gBAAgB,iBAAiB,cAAc,YAAY,SAAS,IAAI;AAEhG,QAAM,kBAAkBA,OAAM;AAAA,IAC5B,CAAC,SAAyB;AACxB,UAAI,WAAW,QAAQ,GAAG;AACxB,oBAAY,UAAU;AACtB;AAAA,MACF;AACA,8BAAwB,MAAM,SAAS,IAAI;AAAA,IAC7C;AAAA,IACA,CAAC,yBAAyB,UAAU,WAAW;AAAA,EACjD;AAEA,QAAM,8BAA8BA,OAAM,YAAY,MAAM;AAC1D,uBAAmB,KAAK;AAAA,EAC1B,GAAG,CAAC,CAAC;AACL,QAAM,qBAAqBA,OAAM,YAAY,MAAM;AACjD,uBAAmB,IAAI;AAAA,EACzB,GAAG,CAAC,CAAC;AAEL,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU;AAAA,MACV;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,2BAA2B;AAAA,MAC3B,gBAAgB;AAAA,MAEhB,8BAAC,iBAAc,eAAe,MAAM,kBAAkB,wBAAwB,OAAO,CAAC,IAAK,GAAG,cAC5F;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAU;AAAA,UACV,YAAY,SAAS,UAAU,aAAa,yBAAyB;AAAA,UAEpE,mBAAS,SAAS,IAAI,CAAC,eACtB;AAAA,YAAC;AAAA;AAAA,cAGC,UAAU;AAAA,cACV;AAAA,cAIA,6BAA6B,kBAAkB,eAAe;AAAA;AAAA,YAPzD,oBAAoB,WAAW,IAAI,IAAI,WAAW;AAAA,UAQzD,CACD;AAAA;AAAA,MACH,GACF;AAAA;AAAA,EACF;AAEJ;AAEA,aAAa,cAAc;AAC3B,MAAM,yBAAyB,SAAS,YAAY;AACpD,uBAAuB,YAAY;",
6
6
  "names": ["React"]
7
7
  }
@@ -5,10 +5,7 @@ import {
5
5
  globalAttributesPropTypes,
6
6
  xstyledPropTypes
7
7
  } from "@elliemae/ds-props-helpers";
8
- import {
9
- DSMenuSpecificPropTypes,
10
- defaultProps as menuSpecificDefaultProps
11
- } from "../../react-desc-prop-types.js";
8
+ import { DSMenuSpecificPropTypes, menuSpecificDefaultProps } from "../../react-desc-prop-types.js";
12
9
  import { DSFlyoutMenuName, FLYOUT_MENU_SLOTS } from "./constants/index.js";
13
10
  const defaultProps = { ...menuSpecificDefaultProps };
14
11
  const DSFlyoutMenuPropTypes = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../scripts/build/transpile/react-shim.js", "../../../../src/parts/DSFlyoutMenu/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 {\n PropTypes,\n getPropsPerSlotPropTypes,\n globalAttributesPropTypes,\n xstyledPropTypes,\n} from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { type useFloatingContext } from '@elliemae/ds-floating-context';\nimport {\n DSMenuSpecificPropTypes,\n defaultProps as menuSpecificDefaultProps,\n type DSMenuButtonT,\n} from '../../react-desc-prop-types.js';\nimport { DSFlyoutMenuName, FLYOUT_MENU_SLOTS } from './constants/index.js';\n\nexport declare namespace DSFlyoutMenuT {\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.MenuSpecificRequiredProps {\n isMenuOpen: boolean;\n itemNode:\n | DSMenuButtonT.MenuNodeWithSubmenuItem\n | DSMenuButtonT.MenuNodeSingleSelectWithSubmenuItem\n | DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem\n | DSMenuButtonT.MenuNodeActivableWithSubmenuItem\n | DSMenuButtonT.MenuNodeRootItem;\n setFloatingRef: ReturnType<typeof useFloatingContext>['refs']['setFloating'];\n floatingStyles: ReturnType<typeof useFloatingContext>['floatingStyles'];\n floatingContext: ReturnType<typeof useFloatingContext>['context'];\n }\n\n export interface DefaultProps extends DSMenuButtonT.MenuSpecifiDefaultProps {}\n\n export interface OptionalProps\n extends TypescriptHelpersT.PropsForGlobalOnSlots<typeof DSFlyoutMenuName, typeof FLYOUT_MENU_SLOTS>,\n DSMenuButtonT.MenuSpecificOptionalProps {}\n\n export interface Props extends RequiredProps, Partial<DefaultProps>, OptionalProps {}\n\n export interface InternalProps extends RequiredProps, DefaultProps, OptionalProps {}\n}\n\nexport const defaultProps: DSFlyoutMenuT.DefaultProps = { ...menuSpecificDefaultProps };\n\nexport const DSFlyoutMenuPropTypes: DSPropTypesSchema<DSFlyoutMenuT.Props> = {\n itemNode: PropTypes.object.description('The parent menu node to render the menu from').isRequired,\n setFloatingRef: PropTypes.func.description(\n 'The function to set the ref of the floating element (from useFloatingContext.refs.setFloating)',\n ).isRequired,\n floatingStyles: PropTypes.object.description(\n 'The styles to apply to the floating element to correctly position it (from useFloatingContext.floatingStyles)',\n ).isRequired,\n floatingContext: PropTypes.object.description(\n 'The context object from useFloatingContext, used to allow FloatingWrapper correct functionality',\n ).isRequired,\n // anchorNode: PropTypes.instanceOf(HTMLElement).isRequired,\n isMenuOpen: PropTypes.bool.description('Wheter or not this particular instance of the flyout menu is open')\n .isRequired,\n ...getPropsPerSlotPropTypes(DSFlyoutMenuName, FLYOUT_MENU_SLOTS),\n ...globalAttributesPropTypes,\n // this component is meant to be used from a ds-menu-button compliant API,\n // for this reason we want to inherit the DSMenuButton props interface that relates to the menu and not the button...\n ...DSMenuSpecificPropTypes,\n ...xstyledPropTypes,\n};\n\nexport const DSFlyoutMenuPropTypesSchema = DSFlyoutMenuPropTypes as unknown as ValidationMap<DSFlyoutMenuT.Props>;\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACEvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA,gBAAgB;AAAA,OAEX;AACP,SAAS,kBAAkB,yBAAyB;AAkC7C,MAAM,eAA2C,EAAE,GAAG,yBAAyB;AAE/E,MAAM,wBAAgE;AAAA,EAC3E,UAAU,UAAU,OAAO,YAAY,8CAA8C,EAAE;AAAA,EACvF,gBAAgB,UAAU,KAAK;AAAA,IAC7B;AAAA,EACF,EAAE;AAAA,EACF,gBAAgB,UAAU,OAAO;AAAA,IAC/B;AAAA,EACF,EAAE;AAAA,EACF,iBAAiB,UAAU,OAAO;AAAA,IAChC;AAAA,EACF,EAAE;AAAA;AAAA,EAEF,YAAY,UAAU,KAAK,YAAY,mEAAmE,EACvG;AAAA,EACH,GAAG,yBAAyB,kBAAkB,iBAAiB;AAAA,EAC/D,GAAG;AAAA;AAAA;AAAA,EAGH,GAAG;AAAA,EACH,GAAG;AACL;AAEO,MAAM,8BAA8B;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-empty-interface */\nimport { type useFloatingContext } from '@elliemae/ds-floating-context';\nimport type { DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';\nimport {\n PropTypes,\n getPropsPerSlotPropTypes,\n globalAttributesPropTypes,\n xstyledPropTypes,\n} from '@elliemae/ds-props-helpers';\nimport { type TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';\nimport { DSMenuSpecificPropTypes, menuSpecificDefaultProps, type DSMenuButtonT } from '../../react-desc-prop-types.js';\nimport { DSFlyoutMenuName, FLYOUT_MENU_SLOTS } from './constants/index.js';\n\nexport declare namespace DSFlyoutMenuT {\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.MenuSpecificRequiredProps {\n isMenuOpen: boolean;\n itemNode:\n | DSMenuButtonT.MenuNodeWithSubmenuItem\n | DSMenuButtonT.MenuNodeSingleSelectWithSubmenuItem\n | DSMenuButtonT.MenuNodeMultipleSelectWithSubmenuItem\n | DSMenuButtonT.MenuNodeActivableWithSubmenuItem\n | DSMenuButtonT.MenuNodeRootItem;\n setFloatingRef: ReturnType<typeof useFloatingContext>['refs']['setFloating'];\n floatingStyles: ReturnType<typeof useFloatingContext>['floatingStyles'];\n floatingContext: ReturnType<typeof useFloatingContext>['context'];\n }\n\n export interface DefaultProps extends DSMenuButtonT.MenuSpecifiDefaultProps {}\n\n export interface OptionalProps\n extends TypescriptHelpersT.PropsForGlobalOnSlots<typeof DSFlyoutMenuName, typeof FLYOUT_MENU_SLOTS>,\n DSMenuButtonT.MenuSpecificOptionalProps {}\n\n export interface Props extends RequiredProps, Partial<DefaultProps>, OptionalProps {}\n\n export interface InternalProps extends RequiredProps, DefaultProps, OptionalProps {}\n}\n\nexport const defaultProps: DSFlyoutMenuT.DefaultProps = { ...menuSpecificDefaultProps };\n\nexport const DSFlyoutMenuPropTypes: DSPropTypesSchema<DSFlyoutMenuT.Props> = {\n itemNode: PropTypes.object.description('The parent menu node to render the menu from').isRequired,\n setFloatingRef: PropTypes.func.description(\n 'The function to set the ref of the floating element (from useFloatingContext.refs.setFloating)',\n ).isRequired,\n floatingStyles: PropTypes.object.description(\n 'The styles to apply to the floating element to correctly position it (from useFloatingContext.floatingStyles)',\n ).isRequired,\n floatingContext: PropTypes.object.description(\n 'The context object from useFloatingContext, used to allow FloatingWrapper correct functionality',\n ).isRequired,\n // anchorNode: PropTypes.instanceOf(HTMLElement).isRequired,\n isMenuOpen: PropTypes.bool.description('Wheter or not this particular instance of the flyout menu is open')\n .isRequired,\n ...getPropsPerSlotPropTypes(DSFlyoutMenuName, FLYOUT_MENU_SLOTS),\n ...globalAttributesPropTypes,\n // this component is meant to be used from a ds-menu-button compliant API,\n // for this reason we want to inherit the DSMenuButton props interface that relates to the menu and not the button...\n ...DSMenuSpecificPropTypes,\n ...xstyledPropTypes,\n};\n\nexport const DSFlyoutMenuPropTypesSchema = DSFlyoutMenuPropTypes as unknown as ValidationMap<DSFlyoutMenuT.Props>;\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACGvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,yBAAyB,gCAAoD;AACtF,SAAS,kBAAkB,yBAAyB;AAkC7C,MAAM,eAA2C,EAAE,GAAG,yBAAyB;AAE/E,MAAM,wBAAgE;AAAA,EAC3E,UAAU,UAAU,OAAO,YAAY,8CAA8C,EAAE;AAAA,EACvF,gBAAgB,UAAU,KAAK;AAAA,IAC7B;AAAA,EACF,EAAE;AAAA,EACF,gBAAgB,UAAU,OAAO;AAAA,IAC/B;AAAA,EACF,EAAE;AAAA,EACF,iBAAiB,UAAU,OAAO;AAAA,IAChC;AAAA,EACF,EAAE;AAAA;AAAA,EAEF,YAAY,UAAU,KAAK,YAAY,mEAAmE,EACvG;AAAA,EACH,GAAG,yBAAyB,kBAAkB,iBAAiB;AAAA,EAC/D,GAAG;AAAA;AAAA;AAAA,EAGH,GAAG;AAAA,EACH,GAAG;AACL;AAEO,MAAM,8BAA8B;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,56 @@
1
+ import * as React from "react";
2
+ import { useEffect } from "react";
3
+ import { isActivableNode, isSelectionableNode } from "../../../utils/nodesTypeguardsAndGetters.js";
4
+ const useAdvancedValidation = ({ propsWithDefault }) => {
5
+ const { onActivateItem, onItemSelected, selectedNodes, optionsTree } = propsWithDefault;
6
+ useEffect(() => {
7
+ const allNodes = optionsTree.flatten();
8
+ const nodesByDsId = {};
9
+ const duplicatedIdsNodes = {};
10
+ let hasActivation = false;
11
+ let hasSelection = false;
12
+ let hasDuplicateDsId = false;
13
+ allNodes.forEach((node) => {
14
+ const { dsId } = node;
15
+ if (!Array.isArray(nodesByDsId[dsId])) {
16
+ nodesByDsId[dsId] = [node];
17
+ } else {
18
+ nodesByDsId[dsId].push(node);
19
+ hasDuplicateDsId = true;
20
+ duplicatedIdsNodes[dsId] = nodesByDsId[dsId];
21
+ }
22
+ if (isActivableNode(node)) {
23
+ hasActivation = true;
24
+ }
25
+ if (isSelectionableNode(node)) {
26
+ hasSelection = true;
27
+ }
28
+ });
29
+ if (hasDuplicateDsId) {
30
+ console.log("duplicatedIdsNodes", duplicatedIdsNodes);
31
+ throw new Error(
32
+ `DSMenuButton:
33
+ The following dsId are repeated:
34
+ ${Object.keys(duplicatedIdsNodes).join(
35
+ "\n"
36
+ )}
37
+
38
+ The dsId must be unique.`
39
+ );
40
+ }
41
+ if (hasActivation && !onActivateItem) {
42
+ throw new Error(
43
+ "DSMenuButton:\nSome items are activable, but onActivateItem is not provided.\n\nYou must provide onActivateItem to handle activation."
44
+ );
45
+ }
46
+ if (hasSelection && (!onItemSelected || !selectedNodes)) {
47
+ throw new Error(
48
+ "DSMenuButton:\nSome items are selectionable, but onItemSelected or selectedNodes are not provided.\n\nYou must provide onItemSelected and selectedNodes to handle selection."
49
+ );
50
+ }
51
+ }, [onActivateItem, onItemSelected, selectedNodes, optionsTree]);
52
+ };
53
+ export {
54
+ useAdvancedValidation
55
+ };
56
+ //# sourceMappingURL=useAdvancedValidation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../scripts/build/transpile/react-shim.js", "../../../../../src/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect } from 'react';\nimport { type DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport { type DSMenuBehaviouralContextProviderT } from '../react-desc-prop-types.js';\nimport { isActivableNode, isSelectionableNode } from '../../../utils/nodesTypeguardsAndGetters.js';\n\ntype UseAdvancedValidationConfigT = {\n propsWithDefault: DSMenuBehaviouralContextProviderT.InternalProps;\n};\nexport const useAdvancedValidation = ({ propsWithDefault }: UseAdvancedValidationConfigT): void => {\n const { onActivateItem, onItemSelected, selectedNodes, optionsTree } = propsWithDefault;\n // =============================================================================\n // SPECIAL VALIDATIONS\n // =============================================================================\n // We iterate onces, validate everything in one go for performance reasons\n // =============================================================================\n // - No repeated dsId\n // (only validating focusableNodes may work, but to avoid complexity we validate the whole tree)\n // - if any item is \"selectionable\", selection IoC props must be provided\n // - if any item is \"activable\", activation IoC props must be provided\n // =============================================================================\n // we want to validate only when dependencies change, should improve performance if app is using memoization\n // and be virtually a free overhead if the app is not using memoization\n // =============================================================================\n useEffect(() => {\n const allNodes = optionsTree.flatten();\n const nodesByDsId: { [key: DSMenuButtonT.MenuNode['dsId']]: DSMenuButtonT.MenuNode[] } = {};\n const duplicatedIdsNodes: { [key: DSMenuButtonT.MenuNode['dsId']]: DSMenuButtonT.MenuNode[] } = {};\n let hasActivation = false;\n let hasSelection = false;\n let hasDuplicateDsId = false;\n allNodes.forEach((node) => {\n const { dsId } = node;\n if (!Array.isArray(nodesByDsId[dsId])) {\n nodesByDsId[dsId] = [node];\n } else {\n nodesByDsId[dsId].push(node);\n hasDuplicateDsId = true;\n duplicatedIdsNodes[dsId] = nodesByDsId[dsId];\n }\n\n if (isActivableNode(node)) {\n hasActivation = true;\n }\n if (isSelectionableNode(node)) {\n hasSelection = true;\n }\n });\n if (hasDuplicateDsId) {\n // eslint-disable-next-line no-console\n console.log('duplicatedIdsNodes', duplicatedIdsNodes);\n throw new Error(\n `DSMenuButton:\\nThe following dsId are repeated:\\n${Object.keys(duplicatedIdsNodes).join(\n '\\n',\n )}\\n\\nThe dsId must be unique.`,\n );\n }\n if (hasActivation && !onActivateItem) {\n throw new Error(\n 'DSMenuButton:\\nSome items are activable, but onActivateItem is not provided.\\n\\nYou must provide onActivateItem to handle activation.',\n );\n }\n if (hasSelection && (!onItemSelected || !selectedNodes)) {\n throw new Error(\n 'DSMenuButton:\\nSome items are selectionable, but onItemSelected or selectedNodes are not provided.\\n\\nYou must provide onItemSelected and selectedNodes to handle selection.',\n );\n }\n }, [onActivateItem, onItemSelected, selectedNodes, optionsTree]);\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,iBAAiB;AAG1B,SAAS,iBAAiB,2BAA2B;AAK9C,MAAM,wBAAwB,CAAC,EAAE,iBAAiB,MAA0C;AACjG,QAAM,EAAE,gBAAgB,gBAAgB,eAAe,YAAY,IAAI;AAcvE,YAAU,MAAM;AACd,UAAM,WAAW,YAAY,QAAQ;AACrC,UAAM,cAAmF,CAAC;AAC1F,UAAM,qBAA0F,CAAC;AACjG,QAAI,gBAAgB;AACpB,QAAI,eAAe;AACnB,QAAI,mBAAmB;AACvB,aAAS,QAAQ,CAAC,SAAS;AACzB,YAAM,EAAE,KAAK,IAAI;AACjB,UAAI,CAAC,MAAM,QAAQ,YAAY,IAAI,CAAC,GAAG;AACrC,oBAAY,IAAI,IAAI,CAAC,IAAI;AAAA,MAC3B,OAAO;AACL,oBAAY,IAAI,EAAE,KAAK,IAAI;AAC3B,2BAAmB;AACnB,2BAAmB,IAAI,IAAI,YAAY,IAAI;AAAA,MAC7C;AAEA,UAAI,gBAAgB,IAAI,GAAG;AACzB,wBAAgB;AAAA,MAClB;AACA,UAAI,oBAAoB,IAAI,GAAG;AAC7B,uBAAe;AAAA,MACjB;AAAA,IACF,CAAC;AACD,QAAI,kBAAkB;AAEpB,cAAQ,IAAI,sBAAsB,kBAAkB;AACpD,YAAM,IAAI;AAAA,QACR;AAAA;AAAA,EAAoD,OAAO,KAAK,kBAAkB,EAAE;AAAA,UAClF;AAAA,QACF,CAAC;AAAA;AAAA;AAAA,MACH;AAAA,IACF;AACA,QAAI,iBAAiB,CAAC,gBAAgB;AACpC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI,iBAAiB,CAAC,kBAAkB,CAAC,gBAAgB;AACvD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,gBAAgB,gBAAgB,eAAe,WAAW,CAAC;AACjE;",
6
+ "names": []
7
+ }