@elliemae/ds-menu-button 3.46.0-rc.1 → 3.46.0-rc.3
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/cjs/config/useMenuButton.js +4 -17
- package/dist/cjs/config/useMenuButton.js.map +2 -2
- package/dist/cjs/config/useSplitInherithedProps.js +31 -15
- package/dist/cjs/config/useSplitInherithedProps.js.map +2 -2
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/parts/DSFlyoutMenu/DSFlyoutMenu.js +11 -1
- package/dist/cjs/parts/DSFlyoutMenu/DSFlyoutMenu.js.map +2 -2
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js +86 -0
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js.map +7 -0
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js +2 -0
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js.map +2 -2
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js +56 -29
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js.map +2 -2
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js +3 -0
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js.map +2 -2
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js +14 -14
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js.map +1 -1
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js +2 -2
- package/dist/cjs/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js.map +1 -1
- package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableMenuItem.js +9 -4
- package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableMenuItem.js.map +2 -2
- package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js +9 -4
- package/dist/cjs/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js.map +2 -2
- package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js +11 -6
- package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js.map +2 -2
- package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js +14 -7
- package/dist/cjs/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js.map +2 -2
- package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js +11 -6
- package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js.map +2 -2
- package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js +11 -6
- package/dist/cjs/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js.map +2 -2
- package/dist/cjs/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js +9 -4
- package/dist/cjs/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js.map +2 -2
- package/dist/cjs/react-desc-prop-types.js +9 -11
- package/dist/cjs/react-desc-prop-types.js.map +2 -2
- package/dist/cjs/utils/nodesTypeguardsAndGetters.js +18 -56
- package/dist/cjs/utils/nodesTypeguardsAndGetters.js.map +2 -2
- package/dist/esm/config/useMenuButton.js +4 -17
- package/dist/esm/config/useMenuButton.js.map +2 -2
- package/dist/esm/config/useSplitInherithedProps.js +31 -15
- package/dist/esm/config/useSplitInherithedProps.js.map +2 -2
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/parts/DSFlyoutMenu/DSFlyoutMenu.js +11 -1
- package/dist/esm/parts/DSFlyoutMenu/DSFlyoutMenu.js.map +2 -2
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js +56 -0
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.js.map +7 -0
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js +2 -0
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js.map +2 -2
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js +56 -29
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.js.map +2 -2
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js +3 -0
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.js.map +2 -2
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js +14 -14
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.js.map +1 -1
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js +2 -2
- package/dist/esm/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.js.map +1 -1
- package/dist/esm/parts/DSMenuItemRendererFactory/ActivableMenuItem.js +9 -4
- package/dist/esm/parts/DSMenuItemRendererFactory/ActivableMenuItem.js.map +2 -2
- package/dist/esm/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js +9 -4
- package/dist/esm/parts/DSMenuItemRendererFactory/ActivableWithSubmenuMenuItem.js.map +2 -2
- package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js +11 -6
- package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectMenuItem.js.map +2 -2
- package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js +14 -7
- package/dist/esm/parts/DSMenuItemRendererFactory/MultipleSelectWithSubmenuMenuItem.js.map +2 -2
- package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js +11 -6
- package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectMenuItem.js.map +2 -2
- package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js +11 -6
- package/dist/esm/parts/DSMenuItemRendererFactory/SingleSelectWithSubmenuMenuItem.js.map +2 -2
- package/dist/esm/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js +9 -4
- package/dist/esm/parts/DSMenuItemRendererFactory/WithSubmenuMenuItem.js.map +2 -2
- package/dist/esm/react-desc-prop-types.js +9 -11
- package/dist/esm/react-desc-prop-types.js.map +2 -2
- package/dist/esm/utils/nodesTypeguardsAndGetters.js +18 -56
- package/dist/esm/utils/nodesTypeguardsAndGetters.js.map +2 -2
- package/dist/types/config/useSplitInherithedProps.d.ts +8 -8
- package/dist/types/index.d.ts +1 -1
- package/dist/types/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.d.ts +6 -0
- package/dist/types/parts/DSMenuBehaviouralContextProvider/config/useMenuItemEventsHandlers.d.ts +2 -1
- package/dist/types/parts/DSMenuBehaviouralContextProvider/react-desc-prop-types.d.ts +1 -0
- package/dist/types/parts/DSMenuBehaviouralContextProvider/utils/multipleSelectionHelpers.d.ts +3 -3
- package/dist/types/parts/DSMenuBehaviouralContextProvider/utils/singleSelectionHelpers.d.ts +2 -2
- package/dist/types/react-desc-prop-types.d.ts +15 -15
- package/dist/types/utils/nodesTypeguardsAndGetters.d.ts +113 -1
- package/package.json +13 -13
@@ -32,14 +32,14 @@ __export(useMenuButton_exports, {
|
|
32
32
|
});
|
33
33
|
module.exports = __toCommonJS(useMenuButton_exports);
|
34
34
|
var React = __toESM(require("react"));
|
35
|
-
var import_react = __toESM(require("react"));
|
36
35
|
var import_ds_props_helpers = require("@elliemae/ds-props-helpers");
|
36
|
+
var import_react = __toESM(require("react"));
|
37
37
|
var import_uid = require("uid");
|
38
38
|
var import_react_desc_prop_types = require("../react-desc-prop-types.js");
|
39
|
-
var import_useValidateProps = require("./useValidateProps.js");
|
40
|
-
var import_useSplitInherithedProps = require("./useSplitInherithedProps.js");
|
41
|
-
var import_useOptionsArrayToDsTree = require("../utils/useOptionsArrayToDsTree.js");
|
42
39
|
var import_nodesTypeguardsAndGetters = require("../utils/nodesTypeguardsAndGetters.js");
|
40
|
+
var import_useOptionsArrayToDsTree = require("../utils/useOptionsArrayToDsTree.js");
|
41
|
+
var import_useSplitInherithedProps = require("./useSplitInherithedProps.js");
|
42
|
+
var import_useValidateProps = require("./useValidateProps.js");
|
43
43
|
const useMenuButton = (propsFromUser) => {
|
44
44
|
const propsWithDefault = (0, import_ds_props_helpers.useMemoMergePropsWithDefault)(propsFromUser, import_react_desc_prop_types.defaultProps);
|
45
45
|
(0, import_useValidateProps.useValidateProps)(propsWithDefault, import_react_desc_prop_types.DSMenuButtonPropTypes);
|
@@ -63,19 +63,6 @@ const useMenuButton = (propsFromUser) => {
|
|
63
63
|
focusableNodes,
|
64
64
|
treeRootNode
|
65
65
|
});
|
66
|
-
const allNodes = treeRootNode.flatten();
|
67
|
-
const nodesWithRepeatedDsId = allNodes.filter(
|
68
|
-
(node, index, self) => self.findIndex((n) => n.dsId === node.dsId) !== index
|
69
|
-
);
|
70
|
-
if (nodesWithRepeatedDsId.length > 0) {
|
71
|
-
throw new Error(
|
72
|
-
`DSMenuButton:
|
73
|
-
The following dsId are repeated:
|
74
|
-
${nodesWithRepeatedDsId.map((n) => n.dsId).join("\n ")}
|
75
|
-
|
76
|
-
The dsId must be unique.`
|
77
|
-
);
|
78
|
-
}
|
79
66
|
return import_react.default.useMemo(
|
80
67
|
() => ({
|
81
68
|
propsWithDefault,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../../../src/config/useMenuButton.ts", "../../../../../../scripts/build/transpile/react-shim.js"],
|
4
|
-
"sourcesContent": ["
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;
|
4
|
+
"sourcesContent": ["/* eslint-disable max-statements */\nimport { useGetXstyledProps, useMemoMergePropsWithDefault } from '@elliemae/ds-props-helpers';\nimport React from 'react';\nimport { uid } from 'uid';\nimport { DSMenuButtonPropTypes, defaultProps, 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>(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\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 // 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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADCvB,8BAAiE;AACjE,mBAAkB;AAClB,iBAAoB;AACpB,mCAAwE;AACxE,uCAAkC;AAClC,qCAAwC;AACxC,qCAAwC;AACxC,8BAAiC;AAO1B,MAAM,gBAAgB,CAAC,kBAAuC;AAInE,QAAM,uBAAmB,sDAA0D,eAAe,yCAAY;AAC9G,gDAAiB,kBAAkB,kDAAqB;AAIxD,QAAM,mBAAe,4CAAmB,gBAAgB;AAKxD,QAAM,cAAc,aAAAA,QAAM,QAAQ,MAAM,kBAAc,gBAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAYlE,QAAM,EAAE,SAAS,WAAW,IAAI;AAChC,QAAM,eAAkD,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC;AAElG,QAAM,0CAAsC,wDAAwB;AAAA,IAClE,SAAS;AAAA,IACT;AAAA;AAAA;AAAA,EAGF,CAAC,EAAE,QAAQ;AACX,QAAM,eAAe,aAAAA,QAAM;AAAA,IACzB,MAAO,MAAM,QAAQ,UAAU,IAAI,sCAAsC;AAAA,IACzE,CAAC,YAAY,mCAAmC;AAAA,EAClD;AAKA,QAAM,iBAAiB,aAAAA,QAAM,QAAQ,UAAM,oDAAkB,YAAY,GAAG,CAAC,YAAY,CAAC;AAC1F,QAAM,2BAAuB,wDAAwB;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,aAAAA,QAAM;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
|
}
|
@@ -33,7 +33,29 @@ __export(useSplitInherithedProps_exports, {
|
|
33
33
|
module.exports = __toCommonJS(useSplitInherithedProps_exports);
|
34
34
|
var React = __toESM(require("react"));
|
35
35
|
var import_react = __toESM(require("react"));
|
36
|
+
var import_nodesTypeguardsAndGetters = require("../utils/nodesTypeguardsAndGetters.js");
|
36
37
|
var import_resolveRef = require("../utils/resolveRef.js");
|
38
|
+
const convertSelectedOptionsToNodes = (selectedItems, treeRootNode) => {
|
39
|
+
const convertedSelection = [];
|
40
|
+
selectedItems.forEach((selectedItem) => {
|
41
|
+
const needsConversion = !(0, import_nodesTypeguardsAndGetters.isObjectAMenuNode)(selectedItem);
|
42
|
+
if (!needsConversion) {
|
43
|
+
convertedSelection.push(selectedItem);
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
const { dsId } = selectedItem;
|
47
|
+
const selectedNode = treeRootNode.findNode((node) => node.dsId === dsId);
|
48
|
+
if (!selectedNode) {
|
49
|
+
throw new Error(`Selected option with dsId ${dsId} not found in the menu tree.`);
|
50
|
+
}
|
51
|
+
if (!(0, import_nodesTypeguardsAndGetters.isSelectionableNode)(selectedNode)) {
|
52
|
+
console.warn(`Selected option with dsId ${dsId} is not a selectionable node and will be ignored.`);
|
53
|
+
return;
|
54
|
+
}
|
55
|
+
convertedSelection.push(selectedNode);
|
56
|
+
});
|
57
|
+
return convertedSelection;
|
58
|
+
};
|
37
59
|
const useSplitInherithedProps = ({
|
38
60
|
propsWithDefault,
|
39
61
|
focusableNodes,
|
@@ -48,7 +70,6 @@ const useSplitInherithedProps = ({
|
|
48
70
|
onSubmenuToggle,
|
49
71
|
isLoading,
|
50
72
|
isSkeleton,
|
51
|
-
selectedOptions,
|
52
73
|
ItemRenderer,
|
53
74
|
innerRef,
|
54
75
|
selectedItems,
|
@@ -68,6 +89,12 @@ const useSplitInherithedProps = ({
|
|
68
89
|
},
|
69
90
|
[innerRef]
|
70
91
|
);
|
92
|
+
const selectedNodesMap = import_react.default.useMemo(() => {
|
93
|
+
if (selectedItems) {
|
94
|
+
return convertSelectedOptionsToNodes(selectedItems, treeRootNode);
|
95
|
+
}
|
96
|
+
return [];
|
97
|
+
}, [selectedItems, treeRootNode]);
|
71
98
|
const menuSpecificProps = import_react.default.useMemo(
|
72
99
|
() => ({
|
73
100
|
options,
|
@@ -77,26 +104,15 @@ const useSplitInherithedProps = ({
|
|
77
104
|
onSubmenuToggle,
|
78
105
|
isLoading,
|
79
106
|
isSkeleton,
|
80
|
-
selectedOptions,
|
81
107
|
ItemRenderer
|
82
108
|
}),
|
83
|
-
[
|
84
|
-
options,
|
85
|
-
onClickOutside,
|
86
|
-
onOptionClick,
|
87
|
-
openedSubmenus,
|
88
|
-
onSubmenuToggle,
|
89
|
-
isLoading,
|
90
|
-
isSkeleton,
|
91
|
-
selectedOptions,
|
92
|
-
ItemRenderer
|
93
|
-
]
|
109
|
+
[options, onClickOutside, onOptionClick, openedSubmenus, onSubmenuToggle, isLoading, isSkeleton, ItemRenderer]
|
94
110
|
);
|
95
111
|
return import_react.default.useMemo(
|
96
112
|
() => ({
|
97
113
|
menuBehaviouralLayerProps: {
|
98
114
|
buttonDOMNodeRef,
|
99
|
-
|
115
|
+
selectedNodes: selectedNodesMap,
|
100
116
|
focusableNodes,
|
101
117
|
optionsTree: treeRootNode,
|
102
118
|
onDisplayedSubmenuChange,
|
@@ -112,7 +128,7 @@ const useSplitInherithedProps = ({
|
|
112
128
|
}
|
113
129
|
}),
|
114
130
|
[
|
115
|
-
|
131
|
+
selectedNodesMap,
|
116
132
|
focusableNodes,
|
117
133
|
treeRootNode,
|
118
134
|
onDisplayedSubmenuChange,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../../../src/config/useSplitInherithedProps.ts", "../../../../../../scripts/build/transpile/react-shim.js"],
|
4
|
-
"sourcesContent": ["/* *******************************************************\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
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADqBvB,mBAAkB;AAElB,wBAA2B;
|
4
|
+
"sourcesContent": ["/* *******************************************************\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 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 ...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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADqBvB,mBAAkB;AAElB,uCAAuD;AACvD,wBAA2B;AAS3B,MAAM,gCAAgC,CACpC,eACA,iBAC2C;AAC3C,QAAM,qBAA6D,CAAC;AAEpE,gBAAc,QAAQ,CAAC,iBAAiB;AACtC,UAAM,kBAAkB,KAAC,oDAAkB,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,KAAC,sDAAoB,YAAY,GAAG;AACtC,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,mBAAmB,aAAAA,QAAM,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,GAAG;AAAA,EACL,IAAI;AAIJ,QAAM,wBAAmE,aAAAA,QAAM;AAAA,IAC7E,CAAC,SAA4B;AAC3B,uBAAiB,UAAU;AAC3B,UAAI,UAAU;AACZ,0CAAW,UAAU,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAIA,QAAM,mBAAmB,aAAAA,QAAM,QAAQ,MAAM;AAC3C,QAAI,eAAe;AACjB,aAAO,8BAA8B,eAAe,YAAY;AAAA,IAClE;AACA,WAAO,CAAC;AAAA,EACV,GAAG,CAAC,eAAe,YAAY,CAAC;AAEhC,QAAM,oBAAoB,aAAAA,QAAM;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,SAAO,aAAAA,QAAM;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/cjs/index.js
CHANGED
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
29
29
|
var src_exports = {};
|
30
30
|
__export(src_exports, {
|
31
31
|
DSMenuButton: () => import_DSMenuButton.DSMenuButton,
|
32
|
+
DSMenuButtonPropTypes: () => import_react_desc_prop_types.DSMenuButtonPropTypes,
|
32
33
|
DSMenuButtonWithSchema: () => import_DSMenuButton.DSMenuButtonWithSchema,
|
33
34
|
MENU_BUTTON_DATA_TESTID: () => import_constants.MENU_BUTTON_DATA_TESTID,
|
34
35
|
MENU_BUTTON_SLOTS: () => import_constants.MENU_BUTTON_SLOTS,
|
@@ -39,6 +40,7 @@ __export(src_exports, {
|
|
39
40
|
module.exports = __toCommonJS(src_exports);
|
40
41
|
var React = __toESM(require("react"));
|
41
42
|
var import_DSMenuButton = require("./DSMenuButton.js");
|
43
|
+
var import_react_desc_prop_types = require("./react-desc-prop-types.js");
|
42
44
|
var import_constants = require("./constants/index.js");
|
43
45
|
var import_nodesTypeguardsAndGetters = require("./utils/nodesTypeguardsAndGetters.js");
|
44
46
|
var import_useOptionsArrayToDsTree = require("./utils/useOptionsArrayToDsTree.js");
|
package/dist/cjs/index.js.map
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../../src/index.ts", "../../../../../scripts/build/transpile/react-shim.js"],
|
4
|
-
"sourcesContent": ["// 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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADGvB,0BAAqD;
|
4
|
+
"sourcesContent": ["// 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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADGvB,0BAAqD;AACrD,mCAA0D;AAC1D,uBAA6E;AAC7E,uCAAgC;AAChC,qCAAwC;",
|
6
6
|
"names": []
|
7
7
|
}
|
@@ -55,12 +55,14 @@ const StyledMenu = (0, import_ds_system.styled)("div", { name: import_constants.
|
|
55
55
|
padding: 0;
|
56
56
|
margin: 0;
|
57
57
|
`;
|
58
|
+
const NoComponentPlaceholder = () => null;
|
58
59
|
const DSFlyoutMenu = (props) => {
|
59
60
|
const { propsWithDefault, xstyledProps } = (0, import_useFlyoutMenu.useFlyoutMenu)(props);
|
60
61
|
const {
|
61
62
|
instanceUid,
|
62
63
|
globalEventsHelpers: { mainMenuRef, handleSubmenusRefChange }
|
63
64
|
} = import_react.default.useContext(import_MenuBehaviouralContextProviderCTX.MenuBehaviouralContextProviderContext);
|
65
|
+
const [didAnimationEnd, setDidAnimationEnd] = import_react.default.useState(false);
|
64
66
|
const { setFloatingRef, floatingStyles, floatingContext, ItemRenderer, isMenuOpen, itemNode } = propsWithDefault;
|
65
67
|
const handleRefChange = import_react.default.useCallback(
|
66
68
|
(node) => {
|
@@ -72,6 +74,12 @@ const DSFlyoutMenu = (props) => {
|
|
72
74
|
},
|
73
75
|
[handleSubmenusRefChange, itemNode, mainMenuRef]
|
74
76
|
);
|
77
|
+
const handleAnimationStartTrigger = import_react.default.useCallback(() => {
|
78
|
+
setDidAnimationEnd(false);
|
79
|
+
}, []);
|
80
|
+
const handleAnimationEnd = import_react.default.useCallback(() => {
|
81
|
+
setDidAnimationEnd(true);
|
82
|
+
}, []);
|
75
83
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
76
84
|
import_ds_floating_context.FloatingWrapper,
|
77
85
|
{
|
@@ -79,12 +87,14 @@ const DSFlyoutMenu = (props) => {
|
|
79
87
|
floatingStyles,
|
80
88
|
isOpen: isMenuOpen,
|
81
89
|
context: floatingContext,
|
90
|
+
onAnimationStartTriggered: handleAnimationStartTrigger,
|
91
|
+
onAnimationEnd: handleAnimationEnd,
|
82
92
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledWrapper, { getOwnerProps: () => propsWithDefault, getOwnerPropsArguments: () => ({}), ...xstyledProps, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StyledMenu, { role: "menu", innerRef: handleRefChange, children: itemNode.children.map((optionNode) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
83
93
|
import_DSMenuItemRendererFactory.DSMenuItemRendererFactory,
|
84
94
|
{
|
85
95
|
itemNode: optionNode,
|
86
96
|
ItemRenderer,
|
87
|
-
FlyoutMenuCircularDepInject: DSFlyoutMenu
|
97
|
+
FlyoutMenuCircularDepInject: didAnimationEnd ? DSFlyoutMenu : NoComponentPlaceholder
|
88
98
|
},
|
89
99
|
`flyout-menu-item-${optionNode.dsId}-${instanceUid}`
|
90
100
|
)) }) })
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../../../../src/parts/DSFlyoutMenu/DSFlyoutMenu.tsx", "../../../../../../../scripts/build/transpile/react-shim.js"],
|
4
|
-
"sourcesContent": ["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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;
|
4
|
+
"sourcesContent": ["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 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 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 // 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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADmEX;AAnEZ,iCAAgC;AAChC,qBAAqB;AACrB,8BAAyB;AACzB,uBAA2C;AAC3C,mBAAkB;AAElB,+CAAsD;AACtD,uCAA0C;AAC1C,2BAA8B;AAC9B,uBAAoD;AACpD,mCAAgE;AAChE,uCAA2B;AAE3B,MAAM,oBAAgB,yBAAO,qBAAM,EAAE,MAAM,mCAAkB,MAAM,mCAAkB,KAAK,CAAC;AAAA;AAAA;AAAA,aAG9E,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,IAAI;AAAA,IACxC,mCAAkB;AAAA;AAGtB,MAAM,iBAAa,yBAAO,OAAO,EAAE,MAAM,mCAAkB,MAAM,mCAAkB,aAAa,CAAC;AAAA;AAAA;AAAA;AAKjG,MAAM,yBAAyB,MAAM;AAErC,MAAM,eAAyD,CAAC,UAAU;AACxE,QAAM,EAAE,kBAAkB,aAAa,QAAI,oCAAc,KAAK;AAC9D,QAAM;AAAA,IACJ;AAAA,IACA,qBAAqB,EAAE,aAAa,wBAAwB;AAAA,EAC9D,IAAI,aAAAA,QAAM,WAAW,8EAAqC;AAC1D,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,aAAAA,QAAM,SAAS,KAAK;AAElE,QAAM,EAAE,gBAAgB,gBAAgB,iBAAiB,cAAc,YAAY,SAAS,IAAI;AAEhG,QAAM,kBAAkB,aAAAA,QAAM;AAAA,IAC5B,CAAC,SAAyB;AACxB,cAAI,6CAAW,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,8BAA8B,aAAAA,QAAM,YAAY,MAAM;AAC1D,uBAAmB,KAAK;AAAA,EAC1B,GAAG,CAAC,CAAC;AACL,QAAM,qBAAqB,aAAAA,QAAM,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,sDAAC,iBAAc,eAAe,MAAM,kBAAkB,wBAAwB,OAAO,CAAC,IAAK,GAAG,cAC5F,sDAAC,cAAW,MAAK,QAAO,UAAU,iBAC/B,mBAAS,SAAS,IAAI,CAAC,eACtB;AAAA,QAAC;AAAA;AAAA,UAGC,UAAU;AAAA,UACV;AAAA,UAIA,6BAA6B,kBAAkB,eAAe;AAAA;AAAA,QAPzD,oBAAoB,WAAW,IAAI,IAAI,WAAW;AAAA,MAQzD,CACD,GACH,GACF;AAAA;AAAA,EACF;AAEJ;AAEA,aAAa,cAAc;AAC3B,MAAM,6BAAyB,kCAAS,YAAY;AACpD,uBAAuB,YAAY;",
|
6
6
|
"names": ["React"]
|
7
7
|
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __create = Object.create;
|
3
|
+
var __defProp = Object.defineProperty;
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
8
|
+
var __export = (target, all) => {
|
9
|
+
for (var name in all)
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
11
|
+
};
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
14
|
+
for (let key of __getOwnPropNames(from))
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
17
|
+
}
|
18
|
+
return to;
|
19
|
+
};
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
26
|
+
mod
|
27
|
+
));
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
29
|
+
var useAdvancedValidation_exports = {};
|
30
|
+
__export(useAdvancedValidation_exports, {
|
31
|
+
useAdvancedValidation: () => useAdvancedValidation
|
32
|
+
});
|
33
|
+
module.exports = __toCommonJS(useAdvancedValidation_exports);
|
34
|
+
var React = __toESM(require("react"));
|
35
|
+
var import_react = require("react");
|
36
|
+
var import_nodesTypeguardsAndGetters = require("../../../utils/nodesTypeguardsAndGetters.js");
|
37
|
+
const useAdvancedValidation = ({ propsWithDefault }) => {
|
38
|
+
const { onActivateItem, onItemSelected, selectedNodes, optionsTree } = propsWithDefault;
|
39
|
+
(0, import_react.useEffect)(() => {
|
40
|
+
const allNodes = optionsTree.flatten();
|
41
|
+
const nodesByDsId = {};
|
42
|
+
const duplicatedIdsNodes = {};
|
43
|
+
let hasActivation = false;
|
44
|
+
let hasSelection = false;
|
45
|
+
let hasDuplicateDsId = false;
|
46
|
+
allNodes.forEach((node) => {
|
47
|
+
const { dsId } = node;
|
48
|
+
if (!Array.isArray(nodesByDsId[dsId])) {
|
49
|
+
nodesByDsId[dsId] = [node];
|
50
|
+
} else {
|
51
|
+
nodesByDsId[dsId].push(node);
|
52
|
+
hasDuplicateDsId = true;
|
53
|
+
duplicatedIdsNodes[dsId] = nodesByDsId[dsId];
|
54
|
+
}
|
55
|
+
if ((0, import_nodesTypeguardsAndGetters.isActivableNode)(node)) {
|
56
|
+
hasActivation = true;
|
57
|
+
}
|
58
|
+
if ((0, import_nodesTypeguardsAndGetters.isSelectionableNode)(node)) {
|
59
|
+
hasSelection = true;
|
60
|
+
}
|
61
|
+
});
|
62
|
+
if (hasDuplicateDsId) {
|
63
|
+
console.log("duplicatedIdsNodes", duplicatedIdsNodes);
|
64
|
+
throw new Error(
|
65
|
+
`DSMenuButton:
|
66
|
+
The following dsId are repeated:
|
67
|
+
${Object.keys(duplicatedIdsNodes).join(
|
68
|
+
"\n"
|
69
|
+
)}
|
70
|
+
|
71
|
+
The dsId must be unique.`
|
72
|
+
);
|
73
|
+
}
|
74
|
+
if (hasActivation && !onActivateItem) {
|
75
|
+
throw new Error(
|
76
|
+
"DSMenuButton:\nSome items are activable, but onActivateItem is not provided.\n\nYou must provide onActivateItem to handle activation."
|
77
|
+
);
|
78
|
+
}
|
79
|
+
if (hasSelection && (!onItemSelected || !selectedNodes)) {
|
80
|
+
throw new Error(
|
81
|
+
"DSMenuButton:\nSome items are selectionable, but onItemSelected or selectedNodes are not provided.\n\nYou must provide onItemSelected and selectedNodes to handle selection."
|
82
|
+
);
|
83
|
+
}
|
84
|
+
}, [onActivateItem, onItemSelected, selectedNodes, optionsTree]);
|
85
|
+
};
|
86
|
+
//# sourceMappingURL=useAdvancedValidation.js.map
|
@@ -0,0 +1,7 @@
|
|
1
|
+
{
|
2
|
+
"version": 3,
|
3
|
+
"sources": ["../../../../../src/parts/DSMenuBehaviouralContextProvider/config/useAdvancedValidation.ts", "../../../../../../../../scripts/build/transpile/react-shim.js"],
|
4
|
+
"sourcesContent": ["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", "import * as React from 'react';\nexport { React };\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAA0B;AAG1B,uCAAqD;AAK9C,MAAM,wBAAwB,CAAC,EAAE,iBAAiB,MAA0C;AACjG,QAAM,EAAE,gBAAgB,gBAAgB,eAAe,YAAY,IAAI;AAcvE,8BAAU,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,cAAI,kDAAgB,IAAI,GAAG;AACzB,wBAAgB;AAAA,MAClB;AACA,cAAI,sDAAoB,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
|
+
}
|
package/dist/cjs/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.js
CHANGED
@@ -40,6 +40,7 @@ var import_useGlobalEvents = require("./useGlobalEvents.js");
|
|
40
40
|
var import_useMenuItemEventsHandlers = require("./useMenuItemEventsHandlers.js");
|
41
41
|
var import_useMenuOpenStatus = require("./useMenuOpenStatus.js");
|
42
42
|
var import_useValidateProps = require("./useValidateProps.js");
|
43
|
+
var import_useAdvancedValidation = require("./useAdvancedValidation.js");
|
43
44
|
const useMenuBehaviouralContextProvider = (propsFromUser) => {
|
44
45
|
const propsWithDefault = import_react.default.useMemo(
|
45
46
|
() => ({
|
@@ -49,6 +50,7 @@ const useMenuBehaviouralContextProvider = (propsFromUser) => {
|
|
49
50
|
[propsFromUser]
|
50
51
|
);
|
51
52
|
(0, import_useValidateProps.useValidateProps)(propsWithDefault, import_react_desc_prop_types.DSMenuBehaviouralContextProviderPropTypesSchema);
|
53
|
+
(0, import_useAdvancedValidation.useAdvancedValidation)({ propsWithDefault });
|
52
54
|
const { onDisplayedSubmenuChange } = propsWithDefault;
|
53
55
|
const instanceUid = import_react.default.useMemo(() => `ds-menu-behavioural-context-provider-${(0, import_uid.uid)(5)}`, []);
|
54
56
|
const [openedSubItems, setOpenedSubItems] = import_react.default.useState([]);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../../../../../src/parts/DSMenuBehaviouralContextProvider/config/useMenuBehaviouralContextProvider.ts", "../../../../../../../../scripts/build/transpile/react-shim.js"],
|
4
|
-
"sourcesContent": ["import React from 'react';\nimport { uid } from 'uid';\nimport type { DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport {\n DSMenuBehaviouralContextProviderPropTypesSchema,\n defaultProps,\n type DSMenuBehaviouralContextProviderT,\n} from '../react-desc-prop-types.js';\nimport { useFocusTracker } from './useFocusTracker.js';\nimport { useGlobalEvents } from './useGlobalEvents.js';\nimport { useMenuItemEventsHandlers } from './useMenuItemEventsHandlers.js';\nimport { useMenuOpenStatus } from './useMenuOpenStatus.js';\nimport { useValidateProps } from './useValidateProps.js';\n\nexport interface MenuBehaviouralContextProviderCTX\n extends ReturnType<typeof useFocusTracker>,\n ReturnType<typeof useMenuOpenStatus> {\n propsWithDefault: DSMenuBehaviouralContextProviderT.InternalProps;\n instanceUid: string;\n menuItemEventsHandlers: ReturnType<typeof useMenuItemEventsHandlers>;\n openedSubItems: DSMenuButtonT.WithSubmenuMenuNodes[];\n handleChangeOpenedSubItems: Required<DSMenuButtonT.MenuBehaviouralLayerOptionalProps>['onDisplayedSubmenuChange'];\n globalEventsHelpers: ReturnType<typeof useGlobalEvents>;\n}\n\nexport const useMenuBehaviouralContextProvider = (propsFromUser: DSMenuBehaviouralContextProviderT.Props) => {\n // =============================================================================\n // MERGE WITH DEFAULT AND VALIDATE PROPS\n // =============================================================================\n // the component deals with HTML DOM elements,\n // Deep compare of HTML DOM elements is pointless and extremely costly\n // for this specific case, we are better of not using `useMemoMergePropsWithDefault`\n const propsWithDefault = React.useMemo(\n () => ({\n ...defaultProps,\n ...propsFromUser,\n }),\n [propsFromUser],\n ) as DSMenuBehaviouralContextProviderT.InternalProps;\n useValidateProps(propsWithDefault, DSMenuBehaviouralContextProviderPropTypesSchema);\n // =============================================================================\n // XSTYLED PROPS\n // =============================================================================\n // NO XSTYLED PROPS\n // this is 100% a pure logic context provider, it's not a visual component\n\n // =============================================================================\n // AD HOC PER COMPONENT LOGIC\n // =============================================================================\n // custom code goes here, this is an example\n const { onDisplayedSubmenuChange } = propsWithDefault;\n const instanceUid = React.useMemo(() => `ds-menu-behavioural-context-provider-${uid(5)}`, []);\n const [openedSubItems, setOpenedSubItems] = React.useState<DSMenuButtonT.WithSubmenuMenuNodes[]>([]);\n\n const handleChangeOpenedSubItems = React.useCallback<\n Required<DSMenuButtonT.MenuBehaviouralLayerOptionalProps>['onDisplayedSubmenuChange']\n >(\n (newOpenedItems, metainfo) => {\n setOpenedSubItems(newOpenedItems);\n onDisplayedSubmenuChange?.(newOpenedItems, metainfo);\n },\n [onDisplayedSubmenuChange],\n );\n\n // =============================================================================\n // HELPERS HOOKS CONFIGS\n // =============================================================================\n const focusTrackers = useFocusTracker();\n const menuOpenStatus = useMenuOpenStatus({ propsWithDefault, focusTrackers });\n\n // why item events handlers declared in the menu behaviour context provider and not in the menu item itself?\n // 1 - each menu item depending on it's own type (single select, multiple select, with submenu) will have different behaviours\n // having each menu item to declare it's own events handlers would make the single event handler way easier to create per se\n // BUT we would lose the big picture of how the menu items are interacting with each other\n // 2 - having each item to declare it's own event handler repeatdly is a waste of resources (even if it's a useless micro-optimization, it hurts me)\n const menuItemEventsHandlers = useMenuItemEventsHandlers({\n propsWithDefault,\n focusTrackers,\n menuOpenStatus,\n handleChangeOpenedSubItems,\n });\n\n // handle \"onClickOutside\" + on window blur...\n // the helpers contain a way to register the submenu refs/mainmenu refs, used in checking \"if click is outside\"\n const globalEventsHelpers = useGlobalEvents({ menuOpenStatus, handleChangeOpenedSubItems, propsWithDefault });\n\n return React.useMemo<MenuBehaviouralContextProviderCTX>(\n () => ({\n propsWithDefault,\n instanceUid,\n ...focusTrackers,\n ...menuOpenStatus,\n menuItemEventsHandlers,\n openedSubItems,\n handleChangeOpenedSubItems,\n globalEventsHelpers,\n }),\n [\n propsWithDefault,\n instanceUid,\n focusTrackers,\n menuOpenStatus,\n menuItemEventsHandlers,\n openedSubItems,\n handleChangeOpenedSubItems,\n globalEventsHelpers,\n ],\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAkB;AAClB,iBAAoB;AAEpB,mCAIO;AACP,6BAAgC;AAChC,6BAAgC;AAChC,uCAA0C;AAC1C,+BAAkC;AAClC,8BAAiC;
|
4
|
+
"sourcesContent": ["import React from 'react';\nimport { uid } from 'uid';\nimport type { DSMenuButtonT } from '../../../react-desc-prop-types.js';\nimport {\n DSMenuBehaviouralContextProviderPropTypesSchema,\n defaultProps,\n type DSMenuBehaviouralContextProviderT,\n} from '../react-desc-prop-types.js';\nimport { useFocusTracker } from './useFocusTracker.js';\nimport { useGlobalEvents } from './useGlobalEvents.js';\nimport { useMenuItemEventsHandlers } from './useMenuItemEventsHandlers.js';\nimport { useMenuOpenStatus } from './useMenuOpenStatus.js';\nimport { useValidateProps } from './useValidateProps.js';\nimport { useAdvancedValidation } from './useAdvancedValidation.js';\n\nexport interface MenuBehaviouralContextProviderCTX\n extends ReturnType<typeof useFocusTracker>,\n ReturnType<typeof useMenuOpenStatus> {\n propsWithDefault: DSMenuBehaviouralContextProviderT.InternalProps;\n instanceUid: string;\n menuItemEventsHandlers: ReturnType<typeof useMenuItemEventsHandlers>;\n openedSubItems: DSMenuButtonT.WithSubmenuMenuNodes[];\n handleChangeOpenedSubItems: Required<DSMenuButtonT.MenuBehaviouralLayerOptionalProps>['onDisplayedSubmenuChange'];\n globalEventsHelpers: ReturnType<typeof useGlobalEvents>;\n}\n\nexport const useMenuBehaviouralContextProvider = (propsFromUser: DSMenuBehaviouralContextProviderT.Props) => {\n // =============================================================================\n // MERGE WITH DEFAULT AND VALIDATE PROPS\n // =============================================================================\n // the component deals with HTML DOM elements,\n // Deep compare of HTML DOM elements is pointless and extremely costly\n // for this specific case, we are better of not using `useMemoMergePropsWithDefault`\n const propsWithDefault = React.useMemo(\n () => ({\n ...defaultProps,\n ...propsFromUser,\n }),\n [propsFromUser],\n ) as DSMenuBehaviouralContextProviderT.InternalProps;\n useValidateProps(propsWithDefault, DSMenuBehaviouralContextProviderPropTypesSchema);\n\n // =============================================================================\n // SPECIAL VALIDATIONS\n // =============================================================================\n useAdvancedValidation({ propsWithDefault });\n\n // =============================================================================\n // XSTYLED PROPS\n // =============================================================================\n // NO XSTYLED PROPS\n // this is 100% a pure logic context provider, it's not a visual component\n\n // =============================================================================\n // AD HOC PER COMPONENT LOGIC\n // =============================================================================\n // custom code goes here, this is an example\n const { onDisplayedSubmenuChange } = propsWithDefault;\n const instanceUid = React.useMemo(() => `ds-menu-behavioural-context-provider-${uid(5)}`, []);\n const [openedSubItems, setOpenedSubItems] = React.useState<DSMenuButtonT.WithSubmenuMenuNodes[]>([]);\n\n const handleChangeOpenedSubItems = React.useCallback<\n Required<DSMenuButtonT.MenuBehaviouralLayerOptionalProps>['onDisplayedSubmenuChange']\n >(\n (newOpenedItems, metainfo) => {\n setOpenedSubItems(newOpenedItems);\n onDisplayedSubmenuChange?.(newOpenedItems, metainfo);\n },\n [onDisplayedSubmenuChange],\n );\n\n // =============================================================================\n // HELPERS HOOKS CONFIGS\n // =============================================================================\n const focusTrackers = useFocusTracker();\n const menuOpenStatus = useMenuOpenStatus({ propsWithDefault, focusTrackers });\n\n // why item events handlers declared in the menu behaviour context provider and not in the menu item itself?\n // 1 - each menu item depending on it's own type (single select, multiple select, with submenu) will have different behaviours\n // having each menu item to declare it's own events handlers would make the single event handler way easier to create per se\n // BUT we would lose the big picture of how the menu items are interacting with each other\n // 2 - having each item to declare it's own event handler repeatdly is a waste of resources (even if it's a useless micro-optimization, it hurts me)\n const menuItemEventsHandlers = useMenuItemEventsHandlers({\n propsWithDefault,\n focusTrackers,\n menuOpenStatus,\n handleChangeOpenedSubItems,\n });\n\n // handle \"onClickOutside\" + on window blur...\n // the helpers contain a way to register the submenu refs/mainmenu refs, used in checking \"if click is outside\"\n const globalEventsHelpers = useGlobalEvents({ menuOpenStatus, handleChangeOpenedSubItems, propsWithDefault });\n\n return React.useMemo<MenuBehaviouralContextProviderCTX>(\n () => ({\n propsWithDefault,\n instanceUid,\n ...focusTrackers,\n ...menuOpenStatus,\n menuItemEventsHandlers,\n openedSubItems,\n handleChangeOpenedSubItems,\n globalEventsHelpers,\n }),\n [\n propsWithDefault,\n instanceUid,\n focusTrackers,\n menuOpenStatus,\n menuItemEventsHandlers,\n openedSubItems,\n handleChangeOpenedSubItems,\n globalEventsHelpers,\n ],\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADAvB,mBAAkB;AAClB,iBAAoB;AAEpB,mCAIO;AACP,6BAAgC;AAChC,6BAAgC;AAChC,uCAA0C;AAC1C,+BAAkC;AAClC,8BAAiC;AACjC,mCAAsC;AAa/B,MAAM,oCAAoC,CAAC,kBAA2D;AAO3G,QAAM,mBAAmB,aAAAA,QAAM;AAAA,IAC7B,OAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG;AAAA,IACL;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AACA,gDAAiB,kBAAkB,4EAA+C;AAKlF,0DAAsB,EAAE,iBAAiB,CAAC;AAY1C,QAAM,EAAE,yBAAyB,IAAI;AACrC,QAAM,cAAc,aAAAA,QAAM,QAAQ,MAAM,4CAAwC,gBAAI,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5F,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,aAAAA,QAAM,SAA+C,CAAC,CAAC;AAEnG,QAAM,6BAA6B,aAAAA,QAAM;AAAA,IAGvC,CAAC,gBAAgB,aAAa;AAC5B,wBAAkB,cAAc;AAChC,iCAA2B,gBAAgB,QAAQ;AAAA,IACrD;AAAA,IACA,CAAC,wBAAwB;AAAA,EAC3B;AAKA,QAAM,oBAAgB,wCAAgB;AACtC,QAAM,qBAAiB,4CAAkB,EAAE,kBAAkB,cAAc,CAAC;AAO5E,QAAM,6BAAyB,4DAA0B;AAAA,IACvD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAID,QAAM,0BAAsB,wCAAgB,EAAE,gBAAgB,4BAA4B,iBAAiB,CAAC;AAE5G,SAAO,aAAAA,QAAM;AAAA,IACX,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH,GAAG;AAAA,MACH;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,IACF;AAAA,EACF;AACF;",
|
6
6
|
"names": ["React"]
|
7
7
|
}
|
@@ -44,7 +44,7 @@ const useMenuItemEventsHandlers = ({
|
|
44
44
|
menuOpenStatus,
|
45
45
|
handleChangeOpenedSubItems
|
46
46
|
}) => {
|
47
|
-
const { onItemSelected, onActivateItem,
|
47
|
+
const { onItemSelected, onActivateItem, selectedNodes } = propsWithDefault;
|
48
48
|
const { onOpinionatedClose } = menuOpenStatus;
|
49
49
|
const {
|
50
50
|
trackFocusFirstChildItem,
|
@@ -95,10 +95,10 @@ const useMenuItemEventsHandlers = ({
|
|
95
95
|
trackFocusFirstChildItem(pseudoFocusedItemNode);
|
96
96
|
} else {
|
97
97
|
if ((0, import_nodesTypeguardsAndGetters.isMultipleSelectNode)(pseudoFocusedItemNode)) {
|
98
|
-
const newSelection = (0, import_multipleSelectionHelpers.getNewSelectionMultipleSelect)(
|
98
|
+
const newSelection = (0, import_multipleSelectionHelpers.getNewSelectionMultipleSelect)(selectedNodes, pseudoFocusedItemNode);
|
99
99
|
onItemSelected(newSelection, { itemNode: pseudoFocusedItemNode, event });
|
100
100
|
} else if ((0, import_nodesTypeguardsAndGetters.isSingleSelectNode)(pseudoFocusedItemNode)) {
|
101
|
-
const newSelection = (0, import_singleSelectionHelpers.getNewSelectionSingleSelect)(
|
101
|
+
const newSelection = (0, import_singleSelectionHelpers.getNewSelectionSingleSelect)(selectedNodes, pseudoFocusedItemNode);
|
102
102
|
onItemSelected(newSelection, { itemNode: pseudoFocusedItemNode, event });
|
103
103
|
} else {
|
104
104
|
onActivateItem(pseudoFocusedItemNode, { itemNode: pseudoFocusedItemNode, event });
|
@@ -114,7 +114,7 @@ const useMenuItemEventsHandlers = ({
|
|
114
114
|
openSubmenu,
|
115
115
|
handleChangeOpenedSubItems,
|
116
116
|
onOpinionatedClose,
|
117
|
-
|
117
|
+
selectedNodes,
|
118
118
|
onItemSelected,
|
119
119
|
onActivateItem
|
120
120
|
]
|
@@ -127,16 +127,16 @@ const useMenuItemEventsHandlers = ({
|
|
127
127
|
return;
|
128
128
|
}
|
129
129
|
if ((0, import_nodesTypeguardsAndGetters.isMultipleSelectNode)(pseudoFocusedItemNode)) {
|
130
|
-
const newSelection = (0, import_multipleSelectionHelpers.getNewSelectionMultipleSelect)(
|
130
|
+
const newSelection = (0, import_multipleSelectionHelpers.getNewSelectionMultipleSelect)(selectedNodes, pseudoFocusedItemNode);
|
131
131
|
onItemSelected(newSelection, { itemNode: pseudoFocusedItemNode, event });
|
132
132
|
return;
|
133
133
|
}
|
134
134
|
if ((0, import_nodesTypeguardsAndGetters.isSingleSelectNode)(pseudoFocusedItemNode)) {
|
135
|
-
const newSelection = (0, import_singleSelectionHelpers.getNewSelectionSingleSelect)(
|
135
|
+
const newSelection = (0, import_singleSelectionHelpers.getNewSelectionSingleSelect)(selectedNodes, pseudoFocusedItemNode);
|
136
136
|
onItemSelected(newSelection, { itemNode: pseudoFocusedItemNode, event });
|
137
137
|
return;
|
138
138
|
}
|
139
|
-
onActivateItem(pseudoFocusedItemNode, { itemNode: pseudoFocusedItemNode, event });
|
139
|
+
onActivateItem?.(pseudoFocusedItemNode, { itemNode: pseudoFocusedItemNode, event });
|
140
140
|
const metainfo = { itemNode: pseudoFocusedItemNode, event };
|
141
141
|
const newOpenedItems = [];
|
142
142
|
handleChangeOpenedSubItems(newOpenedItems, metainfo);
|
@@ -148,7 +148,7 @@ const useMenuItemEventsHandlers = ({
|
|
148
148
|
onOpinionatedClose,
|
149
149
|
trackFocusFirstChildItem,
|
150
150
|
openSubmenu,
|
151
|
-
|
151
|
+
selectedNodes,
|
152
152
|
onItemSelected
|
153
153
|
]
|
154
154
|
);
|
@@ -247,25 +247,6 @@ const useMenuItemEventsHandlers = ({
|
|
247
247
|
onOpinionatedClose
|
248
248
|
]
|
249
249
|
);
|
250
|
-
const handleFocusableMenuItemOnMouseEnter = import_react.default.useCallback(
|
251
|
-
(itemNode, event) => {
|
252
|
-
if (!(0, import_nodesTypeguardsAndGetters.isFocusableNode)(itemNode)) {
|
253
|
-
console.log("handleFocusableMenuItemOnMouseEnter > itemNode:", itemNode);
|
254
|
-
throw import_Errors.FORBIDDEN_BEHAVIOURS.TRYING_TO_FOCUS_NON_FOCUSABLE_NODE;
|
255
|
-
}
|
256
|
-
trackFocusNode(itemNode);
|
257
|
-
if (!(0, import_nodesTypeguardsAndGetters.isWithSubmenuNode)(itemNode)) {
|
258
|
-
const subMenuNodesPath = (0, import_nodeGettersByCriterias.getSubMenusPath)(itemNode);
|
259
|
-
const metainfo = { itemNode, event };
|
260
|
-
const newOpenedItems = [...subMenuNodesPath];
|
261
|
-
handleChangeOpenedSubItems(newOpenedItems, metainfo);
|
262
|
-
return;
|
263
|
-
}
|
264
|
-
if (itemNode.plainItem.disabled) return;
|
265
|
-
openSubmenu({ itemNode, event });
|
266
|
-
},
|
267
|
-
[handleChangeOpenedSubItems, openSubmenu, trackFocusNode]
|
268
|
-
);
|
269
250
|
const handleFocusableMenuItemClick = import_react.default.useCallback(
|
270
251
|
(event) => {
|
271
252
|
const pseudoFocusedItemNode = focusedElementItemNode.current;
|
@@ -289,13 +270,59 @@ const useMenuItemEventsHandlers = ({
|
|
289
270
|
},
|
290
271
|
[focusedElementItemNode, handleMenuItemSpaceKeyDown, handleChangeOpenedSubItems]
|
291
272
|
);
|
273
|
+
const opinionatedFocusBehaviour = import_react.default.useCallback(
|
274
|
+
// we currently know only one "event" that can trigger this opinionated behaviour
|
275
|
+
// - focus => React.FocusEvent <-- triggered by ATs "focus mode" or "virtual cursor" navigation
|
276
|
+
// NOTE:
|
277
|
+
// mouseEnter => React.MouseEvent
|
278
|
+
// still ends up calling this function, but as a side effect of mouse-enter focusing the item, not as the main event
|
279
|
+
(itemNode, event) => {
|
280
|
+
if (!(0, import_nodesTypeguardsAndGetters.isFocusableNode)(itemNode)) {
|
281
|
+
console.log("handleFocusableMenuItemOnMouseEnter > itemNode:", itemNode);
|
282
|
+
throw import_Errors.FORBIDDEN_BEHAVIOURS.TRYING_TO_FOCUS_NON_FOCUSABLE_NODE;
|
283
|
+
}
|
284
|
+
trackFocusNode(itemNode);
|
285
|
+
if (!(0, import_nodesTypeguardsAndGetters.isWithSubmenuNode)(itemNode)) {
|
286
|
+
const subMenuNodesPath = (0, import_nodeGettersByCriterias.getSubMenusPath)(itemNode);
|
287
|
+
const metainfo = { itemNode, event };
|
288
|
+
const newOpenedItems = [...subMenuNodesPath];
|
289
|
+
handleChangeOpenedSubItems(newOpenedItems, metainfo);
|
290
|
+
return;
|
291
|
+
}
|
292
|
+
if (itemNode.plainItem.disabled) return;
|
293
|
+
openSubmenu({ itemNode, event });
|
294
|
+
},
|
295
|
+
[handleChangeOpenedSubItems, openSubmenu, trackFocusNode]
|
296
|
+
);
|
297
|
+
const handleFocusableMenuItemOnMouseEnter = import_react.default.useCallback(
|
298
|
+
(itemNode) => {
|
299
|
+
if (!(0, import_nodesTypeguardsAndGetters.isFocusableNode)(itemNode)) {
|
300
|
+
console.log("handleFocusableMenuItemOnMouseEnter > itemNode:", itemNode);
|
301
|
+
throw import_Errors.FORBIDDEN_BEHAVIOURS.TRYING_TO_FOCUS_NON_FOCUSABLE_NODE;
|
302
|
+
}
|
303
|
+
trackFocusNode(itemNode);
|
304
|
+
},
|
305
|
+
[trackFocusNode]
|
306
|
+
);
|
307
|
+
const handleFocusableMenuItemNativeFocusEvent = import_react.default.useCallback(
|
308
|
+
(itemNode, event) => {
|
309
|
+
opinionatedFocusBehaviour(itemNode, event);
|
310
|
+
},
|
311
|
+
[opinionatedFocusBehaviour]
|
312
|
+
);
|
292
313
|
return import_react.default.useMemo(
|
293
314
|
() => ({
|
294
315
|
handleFocusableMenuItemKeyDown,
|
295
316
|
handleFocusableMenuItemClick,
|
296
|
-
handleFocusableMenuItemOnMouseEnter
|
317
|
+
handleFocusableMenuItemOnMouseEnter,
|
318
|
+
handleFocusableMenuItemNativeFocusEvent
|
297
319
|
}),
|
298
|
-
[
|
320
|
+
[
|
321
|
+
handleFocusableMenuItemClick,
|
322
|
+
handleFocusableMenuItemKeyDown,
|
323
|
+
handleFocusableMenuItemNativeFocusEvent,
|
324
|
+
handleFocusableMenuItemOnMouseEnter
|
325
|
+
]
|
299
326
|
);
|
300
327
|
};
|
301
328
|
//# sourceMappingURL=useMenuItemEventsHandlers.js.map
|