@sap-ux/ui-components 1.14.0 → 1.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/dist/components/Icons.d.ts +150 -150
  2. package/dist/components/Icons.js +628 -628
  3. package/dist/components/UIActionCallout/UIActionCallout.d.ts +64 -64
  4. package/dist/components/UIActionCallout/UIActionCallout.js +67 -67
  5. package/dist/components/UIActionCallout/index.d.ts +1 -1
  6. package/dist/components/UIActionCallout/index.js +17 -17
  7. package/dist/components/UIBreadcrumb/UIBreadcrumb.d.ts +23 -23
  8. package/dist/components/UIBreadcrumb/UIBreadcrumb.js +38 -38
  9. package/dist/components/UIBreadcrumb/index.d.ts +1 -1
  10. package/dist/components/UIBreadcrumb/index.js +17 -17
  11. package/dist/components/UIButton/UIActionButton.d.ts +23 -23
  12. package/dist/components/UIButton/UIActionButton.js +89 -89
  13. package/dist/components/UIButton/UIDefaultButton.d.ts +41 -23
  14. package/dist/components/UIButton/UIDefaultButton.d.ts.map +1 -1
  15. package/dist/components/UIButton/UIDefaultButton.js +255 -237
  16. package/dist/components/UIButton/UIDefaultButton.js.map +1 -1
  17. package/dist/components/UIButton/UIIconButton.d.ts +37 -37
  18. package/dist/components/UIButton/UIIconButton.js +106 -106
  19. package/dist/components/UIButton/UISmallButton.d.ts +34 -34
  20. package/dist/components/UIButton/UISmallButton.js +115 -115
  21. package/dist/components/UIButton/UISplitButton.d.ts +44 -44
  22. package/dist/components/UIButton/UISplitButton.js +86 -86
  23. package/dist/components/UIButton/index.d.ts +7 -7
  24. package/dist/components/UIButton/index.js +21 -21
  25. package/dist/components/UICallout/CalloutCollisionTransform.d.ts +54 -54
  26. package/dist/components/UICallout/CalloutCollisionTransform.js +160 -160
  27. package/dist/components/UICallout/UICallout.d.ts +48 -48
  28. package/dist/components/UICallout/UICallout.js +122 -122
  29. package/dist/components/UICallout/index.d.ts +2 -2
  30. package/dist/components/UICallout/index.js +18 -18
  31. package/dist/components/UICheckbox/UICheckbox.d.ts +34 -34
  32. package/dist/components/UICheckbox/UICheckbox.js +135 -135
  33. package/dist/components/UICheckbox/index.d.ts +1 -1
  34. package/dist/components/UICheckbox/index.js +17 -17
  35. package/dist/components/UIChoiceGroup/UIChoiceGroup.d.ts +28 -28
  36. package/dist/components/UIChoiceGroup/UIChoiceGroup.js +182 -182
  37. package/dist/components/UIChoiceGroup/index.d.ts +1 -1
  38. package/dist/components/UIChoiceGroup/index.js +17 -17
  39. package/dist/components/UIComboBox/UIComboBox.d.ts +208 -208
  40. package/dist/components/UIComboBox/UIComboBox.js +563 -563
  41. package/dist/components/UIComboBox/index.d.ts +1 -1
  42. package/dist/components/UIComboBox/index.js +17 -17
  43. package/dist/components/UIContextualMenu/UIContextualMenu.d.ts +31 -31
  44. package/dist/components/UIContextualMenu/UIContextualMenu.js +126 -126
  45. package/dist/components/UIContextualMenu/UIHighlightMenuOption.d.ts +19 -19
  46. package/dist/components/UIContextualMenu/UIHighlightMenuOption.js +50 -50
  47. package/dist/components/UIContextualMenu/index.d.ts +2 -2
  48. package/dist/components/UIContextualMenu/index.js +18 -18
  49. package/dist/components/UICreateSelect/UICreateSelect.d.ts +36 -36
  50. package/dist/components/UICreateSelect/UICreateSelect.js +172 -172
  51. package/dist/components/UICreateSelect/index.d.ts +1 -1
  52. package/dist/components/UICreateSelect/index.js +17 -17
  53. package/dist/components/UIDatePicker/UIDatePicker.d.ts +49 -49
  54. package/dist/components/UIDatePicker/UIDatePicker.js +72 -72
  55. package/dist/components/UIDatePicker/index.d.ts +1 -1
  56. package/dist/components/UIDatePicker/index.js +17 -17
  57. package/dist/components/UIDialog/UIDialog.d.ts +118 -118
  58. package/dist/components/UIDialog/UIDialog.js +281 -281
  59. package/dist/components/UIDialog/index.d.ts +1 -1
  60. package/dist/components/UIDialog/index.js +17 -17
  61. package/dist/components/UIDropdown/UIDropdown.d.ts +94 -94
  62. package/dist/components/UIDropdown/UIDropdown.js +224 -224
  63. package/dist/components/UIDropdown/index.d.ts +2 -2
  64. package/dist/components/UIDropdown/index.js +18 -18
  65. package/dist/components/UIDropdown/utils.d.ts +20 -20
  66. package/dist/components/UIDropdown/utils.js +43 -43
  67. package/dist/components/UIFlexibleTable/RowActions.d.ts +16 -16
  68. package/dist/components/UIFlexibleTable/RowActions.js +73 -73
  69. package/dist/components/UIFlexibleTable/RowData.d.ts +16 -16
  70. package/dist/components/UIFlexibleTable/RowData.js +111 -111
  71. package/dist/components/UIFlexibleTable/UIFlexibleTable.d.ts +11 -11
  72. package/dist/components/UIFlexibleTable/UIFlexibleTable.js +280 -280
  73. package/dist/components/UIFlexibleTable/UIFlexibleTableActionButton.d.ts +20 -20
  74. package/dist/components/UIFlexibleTable/UIFlexibleTableActionButton.js +20 -20
  75. package/dist/components/UIFlexibleTable/UIFlexibleTableRow.d.ts +26 -26
  76. package/dist/components/UIFlexibleTable/UIFlexibleTableRow.js +219 -219
  77. package/dist/components/UIFlexibleTable/UIFlexibleTableRowActionButton.d.ts +20 -20
  78. package/dist/components/UIFlexibleTable/UIFlexibleTableRowActionButton.js +19 -19
  79. package/dist/components/UIFlexibleTable/UIFlexibleTableRowNoData.d.ts +15 -15
  80. package/dist/components/UIFlexibleTable/UIFlexibleTableRowNoData.js +28 -28
  81. package/dist/components/UIFlexibleTable/index.d.ts +5 -5
  82. package/dist/components/UIFlexibleTable/index.js +21 -21
  83. package/dist/components/UIFlexibleTable/types.d.ts +143 -143
  84. package/dist/components/UIFlexibleTable/types.js +14 -14
  85. package/dist/components/UIFlexibleTable/utils.d.ts +25 -25
  86. package/dist/components/UIFlexibleTable/utils.js +49 -49
  87. package/dist/components/UIFocusZone/UIFocusTrapZone.d.ts +22 -22
  88. package/dist/components/UIFocusZone/UIFocusTrapZone.js +33 -33
  89. package/dist/components/UIFocusZone/UIFocusZone.d.ts +23 -23
  90. package/dist/components/UIFocusZone/UIFocusZone.js +35 -35
  91. package/dist/components/UIFocusZone/index.d.ts +2 -2
  92. package/dist/components/UIFocusZone/index.js +18 -18
  93. package/dist/components/UIIcon/UIIcon.d.ts +24 -24
  94. package/dist/components/UIIcon/UIIcon.js +37 -37
  95. package/dist/components/UIIcon/index.d.ts +1 -1
  96. package/dist/components/UIIcon/index.js +17 -17
  97. package/dist/components/UIInput/UITextInput.d.ts +51 -51
  98. package/dist/components/UIInput/UITextInput.js +244 -244
  99. package/dist/components/UIInput/index.d.ts +1 -1
  100. package/dist/components/UIInput/index.js +17 -17
  101. package/dist/components/UILabel/UILabel.d.ts +30 -30
  102. package/dist/components/UILabel/UILabel.js +64 -64
  103. package/dist/components/UILabel/index.d.ts +1 -1
  104. package/dist/components/UILabel/index.js +17 -17
  105. package/dist/components/UILink/UILink.d.ts +25 -25
  106. package/dist/components/UILink/UILink.js +71 -71
  107. package/dist/components/UILink/index.d.ts +1 -1
  108. package/dist/components/UILink/index.js +17 -17
  109. package/dist/components/UIList/UIList.d.ts +31 -31
  110. package/dist/components/UIList/UIList.js +120 -120
  111. package/dist/components/UIList/index.d.ts +1 -1
  112. package/dist/components/UIList/index.js +17 -17
  113. package/dist/components/UILoader/UILoader.d.ts +27 -27
  114. package/dist/components/UILoader/UILoader.js +78 -78
  115. package/dist/components/UILoader/index.d.ts +1 -1
  116. package/dist/components/UILoader/index.js +17 -17
  117. package/dist/components/UIMessageBar/UIMessageBar.d.ts +25 -25
  118. package/dist/components/UIMessageBar/UIMessageBar.js +56 -56
  119. package/dist/components/UIMessageBar/index.d.ts +1 -1
  120. package/dist/components/UIMessageBar/index.js +17 -17
  121. package/dist/components/UIModal/UIModal.d.ts +23 -23
  122. package/dist/components/UIModal/UIModal.js +43 -43
  123. package/dist/components/UIModal/index.d.ts +1 -1
  124. package/dist/components/UIModal/index.js +17 -17
  125. package/dist/components/UIOverlay/UIOverlay.d.ts +22 -22
  126. package/dist/components/UIOverlay/UIOverlay.js +38 -38
  127. package/dist/components/UIOverlay/index.d.ts +1 -1
  128. package/dist/components/UIOverlay/index.js +17 -17
  129. package/dist/components/UIPersona/UIPersona.d.ts +27 -27
  130. package/dist/components/UIPersona/UIPersona.js +48 -48
  131. package/dist/components/UIPersona/index.d.ts +1 -1
  132. package/dist/components/UIPersona/index.js +17 -17
  133. package/dist/components/UISearchBox/UISearchBox.d.ts +22 -22
  134. package/dist/components/UISearchBox/UISearchBox.js +155 -155
  135. package/dist/components/UISearchBox/index.d.ts +3 -3
  136. package/dist/components/UISearchBox/index.js +17 -17
  137. package/dist/components/UISection/UISection.d.ts +34 -34
  138. package/dist/components/UISection/UISection.js +44 -44
  139. package/dist/components/UISection/UISections.d.ts +249 -249
  140. package/dist/components/UISection/UISections.js +707 -707
  141. package/dist/components/UISection/UISplitter.d.ts +96 -96
  142. package/dist/components/UISection/UISplitter.js +220 -220
  143. package/dist/components/UISection/index.d.ts +3 -3
  144. package/dist/components/UISection/index.js +19 -19
  145. package/dist/components/UISeparator/UISeparator.d.ts +25 -25
  146. package/dist/components/UISeparator/UISeparator.js +65 -65
  147. package/dist/components/UISeparator/index.d.ts +1 -1
  148. package/dist/components/UISeparator/index.js +17 -17
  149. package/dist/components/UITable/UITable-helper.d.ts +88 -88
  150. package/dist/components/UITable/UITable-helper.js +263 -263
  151. package/dist/components/UITable/UITable.d.ts +213 -213
  152. package/dist/components/UITable/UITable.js +779 -779
  153. package/dist/components/UITable/index.d.ts +2 -2
  154. package/dist/components/UITable/index.js +18 -18
  155. package/dist/components/UITable/types.d.ts +77 -77
  156. package/dist/components/UITable/types.js +28 -28
  157. package/dist/components/UITabs/UITabs.d.ts +28 -28
  158. package/dist/components/UITabs/UITabs.js +70 -70
  159. package/dist/components/UITabs/index.d.ts +1 -1
  160. package/dist/components/UITabs/index.js +17 -17
  161. package/dist/components/UIToggle/UIToggle.d.ts +39 -39
  162. package/dist/components/UIToggle/UIToggle.js +181 -181
  163. package/dist/components/UIToggle/index.d.ts +1 -1
  164. package/dist/components/UIToggle/index.js +17 -17
  165. package/dist/components/UIToggleGroup/UIToggleGroup.d.ts +31 -31
  166. package/dist/components/UIToggleGroup/UIToggleGroup.js +139 -139
  167. package/dist/components/UIToggleGroup/UIToggleGroup.types.d.ts +22 -22
  168. package/dist/components/UIToggleGroup/UIToggleGroup.types.js +2 -2
  169. package/dist/components/UIToggleGroup/UIToggleGroupOption/UIToggleGroupOption.d.ts +25 -25
  170. package/dist/components/UIToggleGroup/UIToggleGroupOption/UIToggleGroupOption.js +77 -77
  171. package/dist/components/UIToggleGroup/UIToggleGroupOption/UIToggleGroupOption.types.d.ts +10 -10
  172. package/dist/components/UIToggleGroup/UIToggleGroupOption/UIToggleGroupOption.types.js +2 -2
  173. package/dist/components/UIToggleGroup/UIToggleGroupOption/index.d.ts +2 -2
  174. package/dist/components/UIToggleGroup/UIToggleGroupOption/index.js +18 -18
  175. package/dist/components/UIToggleGroup/index.d.ts +2 -2
  176. package/dist/components/UIToggleGroup/index.js +18 -18
  177. package/dist/components/UIToolbar/UIToolbar.d.ts +36 -36
  178. package/dist/components/UIToolbar/UIToolbar.js +104 -104
  179. package/dist/components/UIToolbar/UIToolbarColumn.d.ts +7 -7
  180. package/dist/components/UIToolbar/UIToolbarColumn.js +33 -33
  181. package/dist/components/UIToolbar/UIToolbarDivider.d.ts +27 -27
  182. package/dist/components/UIToolbar/UIToolbarDivider.js +52 -52
  183. package/dist/components/UIToolbar/index.d.ts +3 -3
  184. package/dist/components/UIToolbar/index.js +19 -19
  185. package/dist/components/UITooltip/UITooltip.d.ts +29 -29
  186. package/dist/components/UITooltip/UITooltip.js +77 -77
  187. package/dist/components/UITooltip/UITooltipUtils.d.ts +17 -17
  188. package/dist/components/UITooltip/UITooltipUtils.js +45 -45
  189. package/dist/components/UITooltip/index.d.ts +2 -2
  190. package/dist/components/UITooltip/index.js +18 -18
  191. package/dist/components/UITranslationInput/UIFormattedText.d.ts +46 -46
  192. package/dist/components/UITranslationInput/UIFormattedText.js +121 -121
  193. package/dist/components/UITranslationInput/UILoadButton.d.ts +52 -52
  194. package/dist/components/UITranslationInput/UILoadButton.js +109 -109
  195. package/dist/components/UITranslationInput/UITranslationButton.d.ts +14 -14
  196. package/dist/components/UITranslationInput/UITranslationButton.js +88 -88
  197. package/dist/components/UITranslationInput/UITranslationButton.types.d.ts +67 -67
  198. package/dist/components/UITranslationInput/UITranslationButton.types.js +21 -21
  199. package/dist/components/UITranslationInput/UITranslationInput.d.ts +25 -25
  200. package/dist/components/UITranslationInput/UITranslationInput.js +169 -169
  201. package/dist/components/UITranslationInput/UITranslationUtils.d.ts +64 -64
  202. package/dist/components/UITranslationInput/UITranslationUtils.js +168 -168
  203. package/dist/components/UITranslationInput/defaults.d.ts +2 -2
  204. package/dist/components/UITranslationInput/defaults.js +15 -15
  205. package/dist/components/UITranslationInput/index.d.ts +2 -2
  206. package/dist/components/UITranslationInput/index.js +18 -18
  207. package/dist/components/UITreeDropdown/UITreeDropdown.d.ts +265 -265
  208. package/dist/components/UITreeDropdown/UITreeDropdown.js +660 -660
  209. package/dist/components/UITreeDropdown/index.d.ts +1 -1
  210. package/dist/components/UITreeDropdown/index.js +17 -17
  211. package/dist/components/UIVerticalDivider/UIVerticalDivider.d.ts +23 -23
  212. package/dist/components/UIVerticalDivider/UIVerticalDivider.js +41 -41
  213. package/dist/components/UIVerticalDivider/index.d.ts +1 -1
  214. package/dist/components/UIVerticalDivider/index.js +17 -17
  215. package/dist/components/UIVirtualList/UIAutoSizer.d.ts +22 -22
  216. package/dist/components/UIVirtualList/UIAutoSizer.js +33 -33
  217. package/dist/components/UIVirtualList/UICellMeasurer.d.ts +23 -23
  218. package/dist/components/UIVirtualList/UICellMeasurer.js +33 -33
  219. package/dist/components/UIVirtualList/UIVirtualList.d.ts +43 -43
  220. package/dist/components/UIVirtualList/UIVirtualList.js +70 -70
  221. package/dist/components/UIVirtualList/index.d.ts +3 -3
  222. package/dist/components/UIVirtualList/index.js +19 -19
  223. package/dist/components/index.d.ts +38 -38
  224. package/dist/components/index.js +54 -54
  225. package/dist/helper/ValidationMessage/MessageWrapper.d.ts +17 -17
  226. package/dist/helper/ValidationMessage/MessageWrapper.js +34 -34
  227. package/dist/helper/ValidationMessage/index.d.ts +2 -2
  228. package/dist/helper/ValidationMessage/index.js +18 -18
  229. package/dist/helper/ValidationMessage/utils.d.ts +31 -31
  230. package/dist/helper/ValidationMessage/utils.js +121 -121
  231. package/dist/index.d.ts +2 -2
  232. package/dist/index.js +18 -18
  233. package/dist/utilities/DeepMerge.d.ts +10 -10
  234. package/dist/utilities/DeepMerge.js +48 -48
  235. package/dist/utilities/Dom.d.ts +14 -14
  236. package/dist/utilities/Dom.js +23 -23
  237. package/dist/utilities/Focus.d.ts +14 -14
  238. package/dist/utilities/Focus.js +34 -34
  239. package/dist/utilities/Id.d.ts +2 -2
  240. package/dist/utilities/Id.js +5 -5
  241. package/dist/utilities/Keys.d.ts +2 -2
  242. package/dist/utilities/Keys.js +5 -5
  243. package/dist/utilities/index.d.ts +4 -4
  244. package/dist/utilities/index.js +20 -20
  245. package/package.json +1 -1
  246. package/storybook/494.d8aaa4d7.iframe.bundle.js +1 -0
  247. package/storybook/{930.1b3c5615.iframe.bundle.js → 930.3177b0dc.iframe.bundle.js} +2 -2
  248. package/storybook/iframe.html +3 -3
  249. package/storybook/main.5f68bcca.iframe.bundle.js +1 -0
  250. package/storybook/project.json +1 -1
  251. package/storybook/{runtime~main.f52a1710.iframe.bundle.js → runtime~main.a213c0ae.iframe.bundle.js} +1 -1
  252. package/storybook/494.b98920dd.iframe.bundle.js +0 -1
  253. package/storybook/main.4ed99289.iframe.bundle.js +0 -1
@@ -1,661 +1,661 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.UITreeDropdown = exports.EdgePosition = exports.UIDirectionalHint = void 0;
7
- const react_1 = __importDefault(require("react"));
8
- const uuid_1 = __importDefault(require("uuid"));
9
- const react_2 = require("@fluentui/react");
10
- Object.defineProperty(exports, "UIDirectionalHint", { enumerable: true, get: function () { return react_2.DirectionalHint; } });
11
- const UIInput_1 = require("../UIInput");
12
- const UIContextualMenu_1 = require("../UIContextualMenu");
13
- const UIIconButton_1 = require("../UIButton/UIIconButton");
14
- const Icons_1 = require("../Icons");
15
- const ValidationMessage_1 = require("../../helper/ValidationMessage");
16
- require("./UITreeDropdown.scss");
17
- const SELECTOR_CLASSES = {
18
- callout: 'ui-tree-callout',
19
- scrollArea: 'ms-ContextualMenu-container',
20
- splitButton: 'ms-ContextualMenu-splitMenu'
21
- };
22
- var EdgePosition;
23
- (function (EdgePosition) {
24
- EdgePosition["First"] = "First";
25
- EdgePosition["Last"] = "Last";
26
- })(EdgePosition = exports.EdgePosition || (exports.EdgePosition = {}));
27
- const KEYBOARD_KEYS = {
28
- ArrowUp: 'ArrowUp',
29
- ArrowDown: 'ArrowDown',
30
- Enter: 'Enter',
31
- Escape: 'Escape'
32
- };
33
- /**
34
- * UITreeDropdown component.
35
- *
36
- * @exports
37
- * @class UIVerticalDivider
38
- * @extends {React.Component<UITreeDropdownProps, UITreeDropdownState>}
39
- */
40
- class UITreeDropdown extends react_1.default.Component {
41
- /**
42
- * Initializes component properties.
43
- *
44
- * @param {UITreeDropdownProps} props
45
- */
46
- constructor(props) {
47
- super(props);
48
- this.UITreeDropdownRef = react_1.default.createRef();
49
- this.UITreeDropdownFocusZoneRef = react_1.default.createRef();
50
- this.inputRef = react_1.default.createRef();
51
- this.submenuRefs = {};
52
- // Calculated offset for submenu positions
53
- // It is added because root menu can have scrollbar - in result root menu's items width is smaller than root menu.
54
- // In such case(when scrollbar) submenu is positioned near to expand/hovered item and position is not on the edge of root menu.
55
- // Using offset/margin we can do corrections to position of submenu and place it one the edge of root menu.
56
- this.submenuOffset = 0;
57
- this.lastKeyDown = '';
58
- this.componentDidMount = () => {
59
- if (this.props.items.length) {
60
- this.buildItems(this.props.items);
61
- this.setState({ isDisabled: false });
62
- }
63
- };
64
- this.componentDidUpdate = (prevProps) => {
65
- if (this.props.items.length !== prevProps.items.length) {
66
- this.setState({ isHidden: true });
67
- this.setState({ isDisabled: this.props.items.length ? false : true });
68
- this.buildItems(this.props.items);
69
- }
70
- if (this.props.value !== prevProps.value) {
71
- this.setState({ value: this.props.value });
72
- }
73
- // Calculate size for submenu offset
74
- this.calculateSubmenuOffset();
75
- };
76
- /**
77
- * Map the payload.
78
- *
79
- * @param {ItemsProps[]} items
80
- */
81
- this.buildItems = (items) => {
82
- if (this.state.items.length !== items.length) {
83
- items = items.map(this.buildSubItems);
84
- const mapedItems = this.mapValuesToContextMenu(items);
85
- this.setState({
86
- originalItems: mapedItems,
87
- items: mapedItems
88
- });
89
- }
90
- };
91
- /**
92
- * Sub items values and style.
93
- *
94
- * @param {ItemsProps} item
95
- * @returns {ItemsProps}
96
- */
97
- this.buildSubItems = (item) => {
98
- if (item.children && item.children.length) {
99
- item.children = item.children.map((el) => {
100
- const regex = new RegExp(item.value, 'ig');
101
- const value = el.value.search(regex) === -1 ? `${item.value}${this.state.valueSeparator}${el.value}` : el.value;
102
- return {
103
- ...el,
104
- split: false,
105
- value
106
- };
107
- });
108
- }
109
- return item;
110
- };
111
- /**
112
- * Map GD payload to ContextMenu payload.
113
- *
114
- * @param {ItemsProps[]} items
115
- * @param {number} level
116
- * @returns {IContextualMenuItem[]}
117
- */
118
- this.mapValuesToContextMenu = (items, level = 0) => {
119
- return items.map((item) => {
120
- if (item.children && item.children.length) {
121
- item.split = true;
122
- const refId = this.getRefId(item.value, level);
123
- if (!this.submenuRefs[refId]) {
124
- this.submenuRefs[refId] = react_1.default.createRef();
125
- }
126
- item.subMenuProps = {
127
- componentRef: this.submenuRefs[refId],
128
- items: this.mapValuesToContextMenu(item.children, level + 1),
129
- focusZoneProps: {
130
- handleTabKey: react_2.FocusZoneTabbableElements.none,
131
- onFocus: () => {
132
- const openerItem = this.defaultSubmenuFocus?.parent;
133
- if (openerItem && openerItem.item.value === item.value && openerItem.level === level) {
134
- this.focusItemWithValue(this.state.value, this.defaultSubmenuFocus?.parent?.item.subMenuProps?.items);
135
- }
136
- }
137
- }
138
- };
139
- }
140
- return {
141
- ...item,
142
- key: item.value,
143
- text: item.label,
144
- className: 'ui-tree-dropdown-list-item',
145
- onClick: () => this.handleSelection(item.value),
146
- onRenderContent: this.handleRenderContent
147
- };
148
- });
149
- };
150
- /**
151
- * Handle the selected value.
152
- *
153
- * @param {string} value
154
- */
155
- this.handleSelection = (value) => {
156
- this.setState({ hasSelected: true, value: value, valueChanged: false }, () => this.props.onParameterValueChange(value));
157
- };
158
- /**
159
- * Handle the keypress value.
160
- *
161
- * @param {React.KeyboardEvent<HTMLInputElement>} event
162
- */
163
- this.handleKeyPress = (event) => {
164
- switch (event.key) {
165
- case 'Enter':
166
- if (!this.state.isMenuOpen) {
167
- this.toggleMenu(false, event);
168
- }
169
- else {
170
- this.setState({ valueChanged: true });
171
- this.handleSelection(this.state.value ? this.state.value : '');
172
- }
173
- break;
174
- case 'ArrowDown':
175
- if (!this.state.isMenuOpen) {
176
- // Open dropdown contextMenu if closed
177
- this.toggleMenu(false, event);
178
- }
179
- else {
180
- this.focusDropdown(event, event.key);
181
- }
182
- break;
183
- case 'Tab':
184
- if (this.state.isMenuOpen) {
185
- // Close Dropdown if open
186
- this.toggleMenu(true);
187
- }
188
- this.handleSelection(this.state.value ? this.state.value : '');
189
- break;
190
- default: {
191
- // do nothing
192
- }
193
- }
194
- this.lastKeyDown = event.key;
195
- };
196
- /**
197
- * Handle ContextMenu focus.
198
- *
199
- * @param {React.KeyboardEvent<HTMLInputElement>} event
200
- * @param {string} key
201
- */
202
- this.focusDropdown = (event, key) => {
203
- if (this.UITreeDropdownFocusZoneRef) {
204
- if (key === KEYBOARD_KEYS.Enter) {
205
- this.focusItemWithValue(this.state.value, this.state.items);
206
- }
207
- else {
208
- this.UITreeDropdownFocusZoneRef.current?.focus(true);
209
- }
210
- // disable scroll which sometimes triggers
211
- event.preventDefault();
212
- }
213
- };
214
- /**
215
- * Custom handle the render from subMenu to control the highlight and the .is-selected.
216
- *
217
- * @param {IContextualMenuListProps} props
218
- * @param {IContextualMenuItemRenderFunctions} defaultRenders
219
- * @returns { React.ReactNode | null}
220
- */
221
- this.handleRenderContent = (props, defaultRenders) => {
222
- props.item.className = props.item.value === this.props.value ? 'is-selected' : '';
223
- props.item.text = this.highlightQuery(props.item.label, this.state.query);
224
- this.applySubmenuPosition(props.item);
225
- return defaultRenders ? defaultRenders.renderItemName(props) : null;
226
- };
227
- /**
228
- * Custom handle the render to control the highlight and the .is-selected.
229
- *
230
- * @param {IContextualMenuListProps} props
231
- * @param {IRenderFunction<IContextualMenuListProps>} defaultRender
232
- * @returns {JSX.Element | null}
233
- */
234
- this.handleRenderMenuList = (props, defaultRender) => {
235
- let mappedItems = [];
236
- if (props?.items) {
237
- mappedItems = props.items.map((item) => {
238
- item.className = item.value === this.props.value ? 'is-selected' : '';
239
- item.text = this.highlightQuery(item.label, this.state.query);
240
- this.applySubmenuPosition(item);
241
- item.subMenuProps?.items.map((subItem) => {
242
- subItem.className = subItem.value === this.props.value ? 'is-selected' : '';
243
- return subItem;
244
- });
245
- return { ...item };
246
- });
247
- }
248
- return defaultRender ? defaultRender({ ...props, items: mappedItems }) : null;
249
- };
250
- /**
251
- * Handle on/off ContextualMenu.
252
- *
253
- * @param {boolean} status
254
- * @param {React.KeyboardEvent<HTMLInputElement>} event
255
- */
256
- this.toggleMenu = (status, event) => {
257
- if (this.props.readOnly) {
258
- return;
259
- }
260
- this.setState({
261
- isHidden: status
262
- });
263
- const key = event?.key;
264
- //select first item after contextMenu is opened
265
- if (event) {
266
- setTimeout(() => {
267
- event.persist();
268
- this.focusDropdown(event, key);
269
- }, 0);
270
- }
271
- };
272
- /**
273
- * Highlight the search string.
274
- *
275
- * @param {string} text
276
- * @param {string} query
277
- * @returns {JSX.Element}
278
- */
279
- this.highlightQuery = (text, query) => {
280
- return react_1.default.createElement(UIContextualMenu_1.UIHighlightMenuOption, { text: text, query: query });
281
- };
282
- /**
283
- * Filter all options that match the query string.
284
- *
285
- * @param {string} input
286
- * @param {IContextualMenuItem} item
287
- * @returns {boolean}
288
- */
289
- this.filterElement = (input, item) => {
290
- const regex = new RegExp(input, 'ig');
291
- if (item?.children?.length) {
292
- return item.children.filter((item) => this.filterElement(input, item)).length > 0;
293
- }
294
- if (item?.value) {
295
- return item.value.search(regex) !== -1;
296
- }
297
- return false;
298
- };
299
- /**
300
- * Update the query string and the prop value.
301
- *
302
- * @param {React.FormEvent<HTMLInputElement | HTMLTextAreaElement>} event
303
- */
304
- this.handleOnChangeValue = (event) => {
305
- const query = event.target;
306
- this.setState((prevState) => ({
307
- hasSelected: false,
308
- value: query.value,
309
- items: prevState.originalItems.filter((item) => this.filterElement(query.value, item)),
310
- query: query.value,
311
- valueChanged: true
312
- }));
313
- if (!this.state.isMenuOpen) {
314
- this.toggleMenu(false);
315
- }
316
- };
317
- /**
318
- * Method reset states.
319
- *
320
- * @param {Event | React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent} event
321
- */
322
- this.handleDismiss = (event) => {
323
- if (event && 'key' in event && event.key === KEYBOARD_KEYS.Escape) {
324
- this.resetValue();
325
- }
326
- else if (!this.state.hasSelected) {
327
- this.props.onParameterValueChange('');
328
- }
329
- this.setState((prevState) => ({
330
- items: prevState.originalItems,
331
- query: ''
332
- }));
333
- this.toggleMenu(true);
334
- this.originalValue = undefined;
335
- };
336
- /**
337
- * Method applies additional styling for submnu callout.
338
- * It is used to apply scroll width offset - in result submenu should be displayed on the edge of root menu.
339
- *
340
- * @param {IContextualMenuItem} item Context menu item.
341
- */
342
- this.applySubmenuPosition = (item) => {
343
- if (item.subMenuProps?.items) {
344
- if (!item.subMenuProps.calloutProps) {
345
- item.subMenuProps.calloutProps = {};
346
- }
347
- item.subMenuProps.calloutProps.styles = {
348
- root: {
349
- marginLeft: this.submenuOffset
350
- }
351
- };
352
- }
353
- };
354
- this.getCalloutDomRef = (submenu = false) => {
355
- const menuLayerClass = `${SELECTOR_CLASSES.callout}${this.state.uiidInput}`;
356
- const callout = document.querySelector(`.${menuLayerClass}`);
357
- return submenu && callout ? callout.nextSibling : callout;
358
- };
359
- /**
360
- * Method calculates offset size for submenus.
361
- * Calculated offset should be used to position submenu right to edge of root menu.
362
- * - Detects if scrollbar exists.
363
- * - Calculates size of scrollbar and stores it as value for offset.
364
- */
365
- this.calculateSubmenuOffset = () => {
366
- const callout = this.getCalloutDomRef();
367
- if (callout) {
368
- const scrollContainer = callout.querySelector(`.${SELECTOR_CLASSES.scrollArea}`);
369
- this.submenuOffset = 0;
370
- if (scrollContainer && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
371
- this.submenuOffset = scrollContainer.offsetWidth - scrollContainer.clientWidth;
372
- }
373
- }
374
- };
375
- /**
376
- * Method updates state, if focus visible, using arrow keys.
377
- *
378
- * @param {HTMLElement|React.FocusEvent<HTMLElement>} ev
379
- */
380
- this.onFocusElementChanged = (ev) => {
381
- const menuOption = ev.getElementsByClassName('ts-Menu-option');
382
- const isFocusVisible = document.getElementsByClassName('ms-Fabric--isFocusVisible');
383
- if (isFocusVisible.length > 0 && menuOption.length > 0) {
384
- this.setState({
385
- value: ev.value ? ev.value : menuOption[0].innerText,
386
- valueChanged: true
387
- });
388
- }
389
- };
390
- /**
391
- * Method handles window keydown event.
392
- * 1. Stores last keyboard pressed event.
393
- * 2. Disables CircularNavigation for menus.
394
- *
395
- * @param {KeyboardEvent | React.KeyboardEvent<HTMLInputElement>} event
396
- */
397
- this.onWindowKeyDown = (event) => {
398
- this.lastKeyDown = event.key;
399
- // Avoid circular navigation
400
- const activeElement = document.activeElement;
401
- if ([KEYBOARD_KEYS.ArrowDown, KEYBOARD_KEYS.ArrowUp].includes(event.key) && activeElement) {
402
- // Disable CircularNavigation
403
- // There is property in focusZoneProps, but it is overwritten by fluent ui and we can not change it from outside
404
- const positions = this.getEdgePosition(activeElement);
405
- const fromFirst = positions.includes(EdgePosition.First) && event.key === KEYBOARD_KEYS.ArrowUp;
406
- const fromLast = positions.includes(EdgePosition.Last) && event.key === KEYBOARD_KEYS.ArrowDown;
407
- if (fromFirst || fromLast) {
408
- // Circular navigation case.
409
- // Check if first item focused in root menu.
410
- if (fromFirst && activeElement.closest(`.${SELECTOR_CLASSES.callout}${this.state.uiidInput}`)) {
411
- // Focus input field if navigation triggered from first item using ArrowUp
412
- this.inputRef.current?.focus();
413
- }
414
- event.stopPropagation();
415
- event.preventDefault();
416
- }
417
- }
418
- };
419
- /**
420
- * Method handles focus logic if arrow key was pressed.
421
- *
422
- * @param {FocusEvent} event
423
- */
424
- this.handleCustomDownKey = (event) => {
425
- if (this.lastKeyDown.includes('Arrow')) {
426
- this.lastKeyDown = '';
427
- this.onFocusElementChanged(event.target);
428
- }
429
- };
430
- /**
431
- * Method appends custom keydown and focus event listeners when context menu is opened.
432
- */
433
- this.applyCustomKeyDownHandlingEvents = () => {
434
- window.addEventListener('keydown', this.onWindowKeyDown, true);
435
- window.addEventListener('focus', this.handleCustomDownKey, true);
436
- };
437
- /**
438
- * Method removes custom keydown and focus event listeners when context menu is dismissed.
439
- */
440
- this.removeCustomKeyDownHandlingEvents = () => {
441
- window.removeEventListener('keydown', this.onWindowKeyDown, true);
442
- window.removeEventListener('focus', this.handleCustomDownKey, true);
443
- };
444
- /**
445
- * Method receives any menu child element and returns edge positions if item is first or last in rendered menu.
446
- *
447
- * @param {Element} itemElement Item's DOM to check position.
448
- * @returns {EdgePosition[]} Returns positions if element is first or last in menu - also can be both.
449
- */
450
- this.getEdgePosition = (itemElement) => {
451
- const container = itemElement.closest('ul');
452
- const item = itemElement.closest('li');
453
- const position = [];
454
- if (container && item) {
455
- if (container.children[0] === item) {
456
- position.push(EdgePosition.First);
457
- }
458
- if (container.children[container.children.length - 1] === item) {
459
- position.push(EdgePosition.Last);
460
- }
461
- }
462
- return position;
463
- };
464
- /**
465
- * Recursive method finds menu item info object in tree menu items by passed value/key of item.
466
- *
467
- * @param {string} [value] Value/key of item.
468
- * @param {IContextualMenuItem[]} [items] Menu items.
469
- * @param {TreeItemInfo} [parent] Item's parent object.
470
- * @param {number} [level] Level of item in tree structure.
471
- * @returns {TreeItemInfo | undefined} Found menu item.
472
- */
473
- this.findItemByValue = (value, items = [], parent, level = 0) => {
474
- let selectedItem;
475
- for (let i = 0; i < items.length; i++) {
476
- const item = items[i];
477
- const selectedItemTemp = {
478
- item,
479
- index: i,
480
- parent,
481
- level
482
- };
483
- if (item.value === value) {
484
- selectedItem = selectedItemTemp;
485
- }
486
- else if (item.subMenuProps?.items?.length) {
487
- selectedItem = this.findItemByValue(value, item.subMenuProps.items, selectedItemTemp, level + 1);
488
- }
489
- if (selectedItem) {
490
- break;
491
- }
492
- }
493
- return selectedItem;
494
- };
495
- /**
496
- * Method finds DOM node of menu item based on received item object and container DOM.
497
- *
498
- * @param {HTMLElement} container Menu container DOM.
499
- * @param {TreeItemInfo} item Menu item info object.
500
- * @returns {HTMLElement | undefined} Found DOM element of item.
501
- */
502
- this.getItemTarget = (container, item) => {
503
- let itemDom;
504
- const listDom = container.querySelector('.ms-ContextualMenu-list');
505
- if (listDom && listDom.childNodes[item.index]) {
506
- const listItemDom = listDom.childNodes[item.index];
507
- const itemElement = listItemDom.firstChild;
508
- itemDom = itemElement;
509
- }
510
- return itemDom;
511
- };
512
- /**
513
- * Method focuses context menu item based on recieved value/key and menude data(items and hoisted object).
514
- * Method works with any level menu.
515
- *
516
- * @param {string} [value] Value/key of item.
517
- * @param {IContextualMenuItem[]} [items] Target menu items.
518
- */
519
- this.focusItemWithValue = (value, items = []) => {
520
- const selectedItem = this.findItemByValue(value, items);
521
- const callout = this.getCalloutDomRef(!!this.defaultSubmenuFocus);
522
- this.defaultSubmenuFocus = undefined;
523
- if (selectedItem && callout) {
524
- let itemDom;
525
- if (selectedItem.parent) {
526
- // Item is in next level of menu - we need open submenu
527
- const parentItemDom = this.getItemTarget(callout, selectedItem.parent);
528
- if (parentItemDom) {
529
- this.defaultSubmenuFocus = selectedItem;
530
- parentItemDom.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 39, which: 39, bubbles: true }));
531
- }
532
- }
533
- else {
534
- // Item is in first level
535
- itemDom = this.getItemTarget(callout, selectedItem);
536
- }
537
- // Focus target item or focus container while submenu is not opened
538
- if (itemDom) {
539
- itemDom.focus();
540
- }
541
- else {
542
- const menuContainer = callout.querySelector(`.${SELECTOR_CLASSES.scrollArea}`);
543
- menuContainer?.focus();
544
- }
545
- }
546
- };
547
- /**
548
- * Generate unique id for menu component references.
549
- *
550
- * @param {string} value Value of item.
551
- * @param {number} level Level of item in tree structure.
552
- * @returns {string} Id containing value andf level in format "${value}__${level}".
553
- */
554
- this.getRefId = (value, level) => {
555
- return `${value}__${level}`;
556
- };
557
- this.state = {
558
- query: '',
559
- hasSelected: this.props.value ? true : false,
560
- // value has to be set, otherwise react treats this as "uncontrolled" component
561
- // and displays warnings when value is set later on
562
- value: this.props.value || '',
563
- isHidden: true,
564
- originalItems: [],
565
- items: [],
566
- valueSeparator: this.props.valueSeparator || '.',
567
- uiidInput: uuid_1.default.v4(),
568
- isDisabled: this.props.items.length ? false : true,
569
- isMenuOpen: false,
570
- valueChanged: false
571
- };
572
- this.toggleMenu = this.toggleMenu.bind(this);
573
- this.onWindowKeyDown = this.onWindowKeyDown.bind(this);
574
- this.handleCustomDownKey = this.handleCustomDownKey.bind(this);
575
- // Suppress icon warnings, as they are irrelevant
576
- (0, react_2.setIconOptions)({ disableWarnings: true });
577
- }
578
- /**
579
- * Method resets value of dropdown input to original value, which was stored after open.
580
- */
581
- resetValue() {
582
- this.setState({
583
- value: this.originalValue,
584
- valueChanged: false
585
- });
586
- }
587
- /**
588
- * Method returns class names for wrapper element depending on props and component state.
589
- *
590
- * @returns {string} Class names of wrapper element.
591
- */
592
- getClassNames() {
593
- let classNames = `ui-treeDropdown-wrapper ui-treeDropdown-wrapper-menu${this.state.isMenuOpen ? '-open' : '-close'} ui-treeDropdown-wrapper-${this.state.uiidInput}`;
594
- if (this.state.isDisabled) {
595
- classNames += ' disabled';
596
- }
597
- if (this.props.readOnly) {
598
- classNames += ' readonly';
599
- }
600
- return classNames;
601
- }
602
- /**
603
- * @returns {JSX.Element}
604
- */
605
- render() {
606
- const messageInfo = (0, ValidationMessage_1.getMessageInfo)(this.props);
607
- let useTargetWidth = true;
608
- if (this.props.useTargetWidth) {
609
- useTargetWidth = false;
610
- }
611
- return (react_1.default.createElement("div", { className: `ui-treeDropdown ui-treeDropDown-${this.state.uiidInput} ${this.props.label ? 'ui-treeDropdown-with-label' : ''}` },
612
- this.props.label && (react_1.default.createElement("label", { className: `${this.props.required ? 'required' : ''} ${this.state.isDisabled ? 'disabled' : ''}` }, this.props.label)),
613
- react_1.default.createElement("div", { className: this.getClassNames() },
614
- react_1.default.createElement(UIInput_1.UITextInput, { componentRef: this.inputRef, disabled: this.state.isDisabled, readOnly: this.props.readOnly, autoComplete: "off", value: this.state.value, placeholder: this.props.placeholderText, onKeyDown: this.handleKeyPress, onChange: this.handleOnChangeValue, onClick: () => {
615
- this.toggleMenu(false);
616
- }, onFocus: (event) => {
617
- // Select the text of the input
618
- event.target.select();
619
- }, errorMessage: messageInfo.message }),
620
- react_1.default.createElement(UIIconButton_1.UIIconButton, { tabIndex: -1, allowDisabledFocus: true, className: "ui-treeDropdown-caret", iconProps: { iconName: Icons_1.UiIcons.ArrowDown }, onClick: () => {
621
- if (this.state.isHidden) {
622
- // Menu would become visible - focus input
623
- this.inputRef.current?.focus();
624
- }
625
- this.toggleMenu(!this.state.isHidden);
626
- } })),
627
- !this.state.isHidden && (react_1.default.createElement(UIContextualMenu_1.UIContextualMenu, { componentRef: this.UITreeDropdownRef, onRenderMenuList: this.handleRenderMenuList, className: "ui-treeDropDown-context-menu", target: `.ui-treeDropDown-${this.state.uiidInput}`, onMenuOpened: () => {
628
- this.originalValue = this.state.value;
629
- this.applyCustomKeyDownHandlingEvents();
630
- this.setState({
631
- isMenuOpen: true
632
- });
633
- }, onMenuDismissed: () => {
634
- this.removeCustomKeyDownHandlingEvents();
635
- this.setState({ isMenuOpen: false });
636
- if (this.state.valueChanged) {
637
- this.handleSelection(this.state.value ? this.state.value : '');
638
- }
639
- }, useTargetWidth: useTargetWidth, useTargetAsMinWidth: true, onRestoreFocus: (params) => {
640
- params.originalElement?.focus();
641
- }, shouldUpdateWhenHidden: true, items: this.state.items, onDismiss: this.handleDismiss, shouldFocusOnContainer: false, focusZoneProps: {
642
- componentRef: this.UITreeDropdownFocusZoneRef,
643
- handleTabKey: react_2.FocusZoneTabbableElements.none,
644
- isCircularNavigation: false
645
- }, shouldFocusOnMount: false, directionalHint: this.props.directionalHint, calloutProps: {
646
- layerProps: {
647
- className: `${SELECTOR_CLASSES.callout}${this.state.uiidInput}`
648
- },
649
- onLayerMounted: () => {
650
- this.calculateSubmenuOffset();
651
- }
652
- }, styles: {
653
- container: {
654
- maxHeight: 192,
655
- overflowY: 'auto'
656
- }
657
- }, maxWidth: this.props.maxWidth }))));
658
- }
659
- }
660
- exports.UITreeDropdown = UITreeDropdown;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.UITreeDropdown = exports.EdgePosition = exports.UIDirectionalHint = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const uuid_1 = __importDefault(require("uuid"));
9
+ const react_2 = require("@fluentui/react");
10
+ Object.defineProperty(exports, "UIDirectionalHint", { enumerable: true, get: function () { return react_2.DirectionalHint; } });
11
+ const UIInput_1 = require("../UIInput");
12
+ const UIContextualMenu_1 = require("../UIContextualMenu");
13
+ const UIIconButton_1 = require("../UIButton/UIIconButton");
14
+ const Icons_1 = require("../Icons");
15
+ const ValidationMessage_1 = require("../../helper/ValidationMessage");
16
+ require("./UITreeDropdown.scss");
17
+ const SELECTOR_CLASSES = {
18
+ callout: 'ui-tree-callout',
19
+ scrollArea: 'ms-ContextualMenu-container',
20
+ splitButton: 'ms-ContextualMenu-splitMenu'
21
+ };
22
+ var EdgePosition;
23
+ (function (EdgePosition) {
24
+ EdgePosition["First"] = "First";
25
+ EdgePosition["Last"] = "Last";
26
+ })(EdgePosition = exports.EdgePosition || (exports.EdgePosition = {}));
27
+ const KEYBOARD_KEYS = {
28
+ ArrowUp: 'ArrowUp',
29
+ ArrowDown: 'ArrowDown',
30
+ Enter: 'Enter',
31
+ Escape: 'Escape'
32
+ };
33
+ /**
34
+ * UITreeDropdown component.
35
+ *
36
+ * @exports
37
+ * @class UIVerticalDivider
38
+ * @extends {React.Component<UITreeDropdownProps, UITreeDropdownState>}
39
+ */
40
+ class UITreeDropdown extends react_1.default.Component {
41
+ /**
42
+ * Initializes component properties.
43
+ *
44
+ * @param {UITreeDropdownProps} props
45
+ */
46
+ constructor(props) {
47
+ super(props);
48
+ this.UITreeDropdownRef = react_1.default.createRef();
49
+ this.UITreeDropdownFocusZoneRef = react_1.default.createRef();
50
+ this.inputRef = react_1.default.createRef();
51
+ this.submenuRefs = {};
52
+ // Calculated offset for submenu positions
53
+ // It is added because root menu can have scrollbar - in result root menu's items width is smaller than root menu.
54
+ // In such case(when scrollbar) submenu is positioned near to expand/hovered item and position is not on the edge of root menu.
55
+ // Using offset/margin we can do corrections to position of submenu and place it one the edge of root menu.
56
+ this.submenuOffset = 0;
57
+ this.lastKeyDown = '';
58
+ this.componentDidMount = () => {
59
+ if (this.props.items.length) {
60
+ this.buildItems(this.props.items);
61
+ this.setState({ isDisabled: false });
62
+ }
63
+ };
64
+ this.componentDidUpdate = (prevProps) => {
65
+ if (this.props.items.length !== prevProps.items.length) {
66
+ this.setState({ isHidden: true });
67
+ this.setState({ isDisabled: this.props.items.length ? false : true });
68
+ this.buildItems(this.props.items);
69
+ }
70
+ if (this.props.value !== prevProps.value) {
71
+ this.setState({ value: this.props.value });
72
+ }
73
+ // Calculate size for submenu offset
74
+ this.calculateSubmenuOffset();
75
+ };
76
+ /**
77
+ * Map the payload.
78
+ *
79
+ * @param {ItemsProps[]} items
80
+ */
81
+ this.buildItems = (items) => {
82
+ if (this.state.items.length !== items.length) {
83
+ items = items.map(this.buildSubItems);
84
+ const mapedItems = this.mapValuesToContextMenu(items);
85
+ this.setState({
86
+ originalItems: mapedItems,
87
+ items: mapedItems
88
+ });
89
+ }
90
+ };
91
+ /**
92
+ * Sub items values and style.
93
+ *
94
+ * @param {ItemsProps} item
95
+ * @returns {ItemsProps}
96
+ */
97
+ this.buildSubItems = (item) => {
98
+ if (item.children && item.children.length) {
99
+ item.children = item.children.map((el) => {
100
+ const regex = new RegExp(item.value, 'ig');
101
+ const value = el.value.search(regex) === -1 ? `${item.value}${this.state.valueSeparator}${el.value}` : el.value;
102
+ return {
103
+ ...el,
104
+ split: false,
105
+ value
106
+ };
107
+ });
108
+ }
109
+ return item;
110
+ };
111
+ /**
112
+ * Map GD payload to ContextMenu payload.
113
+ *
114
+ * @param {ItemsProps[]} items
115
+ * @param {number} level
116
+ * @returns {IContextualMenuItem[]}
117
+ */
118
+ this.mapValuesToContextMenu = (items, level = 0) => {
119
+ return items.map((item) => {
120
+ if (item.children && item.children.length) {
121
+ item.split = true;
122
+ const refId = this.getRefId(item.value, level);
123
+ if (!this.submenuRefs[refId]) {
124
+ this.submenuRefs[refId] = react_1.default.createRef();
125
+ }
126
+ item.subMenuProps = {
127
+ componentRef: this.submenuRefs[refId],
128
+ items: this.mapValuesToContextMenu(item.children, level + 1),
129
+ focusZoneProps: {
130
+ handleTabKey: react_2.FocusZoneTabbableElements.none,
131
+ onFocus: () => {
132
+ const openerItem = this.defaultSubmenuFocus?.parent;
133
+ if (openerItem && openerItem.item.value === item.value && openerItem.level === level) {
134
+ this.focusItemWithValue(this.state.value, this.defaultSubmenuFocus?.parent?.item.subMenuProps?.items);
135
+ }
136
+ }
137
+ }
138
+ };
139
+ }
140
+ return {
141
+ ...item,
142
+ key: item.value,
143
+ text: item.label,
144
+ className: 'ui-tree-dropdown-list-item',
145
+ onClick: () => this.handleSelection(item.value),
146
+ onRenderContent: this.handleRenderContent
147
+ };
148
+ });
149
+ };
150
+ /**
151
+ * Handle the selected value.
152
+ *
153
+ * @param {string} value
154
+ */
155
+ this.handleSelection = (value) => {
156
+ this.setState({ hasSelected: true, value: value, valueChanged: false }, () => this.props.onParameterValueChange(value));
157
+ };
158
+ /**
159
+ * Handle the keypress value.
160
+ *
161
+ * @param {React.KeyboardEvent<HTMLInputElement>} event
162
+ */
163
+ this.handleKeyPress = (event) => {
164
+ switch (event.key) {
165
+ case 'Enter':
166
+ if (!this.state.isMenuOpen) {
167
+ this.toggleMenu(false, event);
168
+ }
169
+ else {
170
+ this.setState({ valueChanged: true });
171
+ this.handleSelection(this.state.value ? this.state.value : '');
172
+ }
173
+ break;
174
+ case 'ArrowDown':
175
+ if (!this.state.isMenuOpen) {
176
+ // Open dropdown contextMenu if closed
177
+ this.toggleMenu(false, event);
178
+ }
179
+ else {
180
+ this.focusDropdown(event, event.key);
181
+ }
182
+ break;
183
+ case 'Tab':
184
+ if (this.state.isMenuOpen) {
185
+ // Close Dropdown if open
186
+ this.toggleMenu(true);
187
+ }
188
+ this.handleSelection(this.state.value ? this.state.value : '');
189
+ break;
190
+ default: {
191
+ // do nothing
192
+ }
193
+ }
194
+ this.lastKeyDown = event.key;
195
+ };
196
+ /**
197
+ * Handle ContextMenu focus.
198
+ *
199
+ * @param {React.KeyboardEvent<HTMLInputElement>} event
200
+ * @param {string} key
201
+ */
202
+ this.focusDropdown = (event, key) => {
203
+ if (this.UITreeDropdownFocusZoneRef) {
204
+ if (key === KEYBOARD_KEYS.Enter) {
205
+ this.focusItemWithValue(this.state.value, this.state.items);
206
+ }
207
+ else {
208
+ this.UITreeDropdownFocusZoneRef.current?.focus(true);
209
+ }
210
+ // disable scroll which sometimes triggers
211
+ event.preventDefault();
212
+ }
213
+ };
214
+ /**
215
+ * Custom handle the render from subMenu to control the highlight and the .is-selected.
216
+ *
217
+ * @param {IContextualMenuListProps} props
218
+ * @param {IContextualMenuItemRenderFunctions} defaultRenders
219
+ * @returns { React.ReactNode | null}
220
+ */
221
+ this.handleRenderContent = (props, defaultRenders) => {
222
+ props.item.className = props.item.value === this.props.value ? 'is-selected' : '';
223
+ props.item.text = this.highlightQuery(props.item.label, this.state.query);
224
+ this.applySubmenuPosition(props.item);
225
+ return defaultRenders ? defaultRenders.renderItemName(props) : null;
226
+ };
227
+ /**
228
+ * Custom handle the render to control the highlight and the .is-selected.
229
+ *
230
+ * @param {IContextualMenuListProps} props
231
+ * @param {IRenderFunction<IContextualMenuListProps>} defaultRender
232
+ * @returns {JSX.Element | null}
233
+ */
234
+ this.handleRenderMenuList = (props, defaultRender) => {
235
+ let mappedItems = [];
236
+ if (props?.items) {
237
+ mappedItems = props.items.map((item) => {
238
+ item.className = item.value === this.props.value ? 'is-selected' : '';
239
+ item.text = this.highlightQuery(item.label, this.state.query);
240
+ this.applySubmenuPosition(item);
241
+ item.subMenuProps?.items.map((subItem) => {
242
+ subItem.className = subItem.value === this.props.value ? 'is-selected' : '';
243
+ return subItem;
244
+ });
245
+ return { ...item };
246
+ });
247
+ }
248
+ return defaultRender ? defaultRender({ ...props, items: mappedItems }) : null;
249
+ };
250
+ /**
251
+ * Handle on/off ContextualMenu.
252
+ *
253
+ * @param {boolean} status
254
+ * @param {React.KeyboardEvent<HTMLInputElement>} event
255
+ */
256
+ this.toggleMenu = (status, event) => {
257
+ if (this.props.readOnly) {
258
+ return;
259
+ }
260
+ this.setState({
261
+ isHidden: status
262
+ });
263
+ const key = event?.key;
264
+ //select first item after contextMenu is opened
265
+ if (event) {
266
+ setTimeout(() => {
267
+ event.persist();
268
+ this.focusDropdown(event, key);
269
+ }, 0);
270
+ }
271
+ };
272
+ /**
273
+ * Highlight the search string.
274
+ *
275
+ * @param {string} text
276
+ * @param {string} query
277
+ * @returns {JSX.Element}
278
+ */
279
+ this.highlightQuery = (text, query) => {
280
+ return react_1.default.createElement(UIContextualMenu_1.UIHighlightMenuOption, { text: text, query: query });
281
+ };
282
+ /**
283
+ * Filter all options that match the query string.
284
+ *
285
+ * @param {string} input
286
+ * @param {IContextualMenuItem} item
287
+ * @returns {boolean}
288
+ */
289
+ this.filterElement = (input, item) => {
290
+ const regex = new RegExp(input, 'ig');
291
+ if (item?.children?.length) {
292
+ return item.children.filter((item) => this.filterElement(input, item)).length > 0;
293
+ }
294
+ if (item?.value) {
295
+ return item.value.search(regex) !== -1;
296
+ }
297
+ return false;
298
+ };
299
+ /**
300
+ * Update the query string and the prop value.
301
+ *
302
+ * @param {React.FormEvent<HTMLInputElement | HTMLTextAreaElement>} event
303
+ */
304
+ this.handleOnChangeValue = (event) => {
305
+ const query = event.target;
306
+ this.setState((prevState) => ({
307
+ hasSelected: false,
308
+ value: query.value,
309
+ items: prevState.originalItems.filter((item) => this.filterElement(query.value, item)),
310
+ query: query.value,
311
+ valueChanged: true
312
+ }));
313
+ if (!this.state.isMenuOpen) {
314
+ this.toggleMenu(false);
315
+ }
316
+ };
317
+ /**
318
+ * Method reset states.
319
+ *
320
+ * @param {Event | React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent} event
321
+ */
322
+ this.handleDismiss = (event) => {
323
+ if (event && 'key' in event && event.key === KEYBOARD_KEYS.Escape) {
324
+ this.resetValue();
325
+ }
326
+ else if (!this.state.hasSelected) {
327
+ this.props.onParameterValueChange('');
328
+ }
329
+ this.setState((prevState) => ({
330
+ items: prevState.originalItems,
331
+ query: ''
332
+ }));
333
+ this.toggleMenu(true);
334
+ this.originalValue = undefined;
335
+ };
336
+ /**
337
+ * Method applies additional styling for submnu callout.
338
+ * It is used to apply scroll width offset - in result submenu should be displayed on the edge of root menu.
339
+ *
340
+ * @param {IContextualMenuItem} item Context menu item.
341
+ */
342
+ this.applySubmenuPosition = (item) => {
343
+ if (item.subMenuProps?.items) {
344
+ if (!item.subMenuProps.calloutProps) {
345
+ item.subMenuProps.calloutProps = {};
346
+ }
347
+ item.subMenuProps.calloutProps.styles = {
348
+ root: {
349
+ marginLeft: this.submenuOffset
350
+ }
351
+ };
352
+ }
353
+ };
354
+ this.getCalloutDomRef = (submenu = false) => {
355
+ const menuLayerClass = `${SELECTOR_CLASSES.callout}${this.state.uiidInput}`;
356
+ const callout = document.querySelector(`.${menuLayerClass}`);
357
+ return submenu && callout ? callout.nextSibling : callout;
358
+ };
359
+ /**
360
+ * Method calculates offset size for submenus.
361
+ * Calculated offset should be used to position submenu right to edge of root menu.
362
+ * - Detects if scrollbar exists.
363
+ * - Calculates size of scrollbar and stores it as value for offset.
364
+ */
365
+ this.calculateSubmenuOffset = () => {
366
+ const callout = this.getCalloutDomRef();
367
+ if (callout) {
368
+ const scrollContainer = callout.querySelector(`.${SELECTOR_CLASSES.scrollArea}`);
369
+ this.submenuOffset = 0;
370
+ if (scrollContainer && scrollContainer.scrollHeight > scrollContainer.clientHeight) {
371
+ this.submenuOffset = scrollContainer.offsetWidth - scrollContainer.clientWidth;
372
+ }
373
+ }
374
+ };
375
+ /**
376
+ * Method updates state, if focus visible, using arrow keys.
377
+ *
378
+ * @param {HTMLElement|React.FocusEvent<HTMLElement>} ev
379
+ */
380
+ this.onFocusElementChanged = (ev) => {
381
+ const menuOption = ev.getElementsByClassName('ts-Menu-option');
382
+ const isFocusVisible = document.getElementsByClassName('ms-Fabric--isFocusVisible');
383
+ if (isFocusVisible.length > 0 && menuOption.length > 0) {
384
+ this.setState({
385
+ value: ev.value ? ev.value : menuOption[0].innerText,
386
+ valueChanged: true
387
+ });
388
+ }
389
+ };
390
+ /**
391
+ * Method handles window keydown event.
392
+ * 1. Stores last keyboard pressed event.
393
+ * 2. Disables CircularNavigation for menus.
394
+ *
395
+ * @param {KeyboardEvent | React.KeyboardEvent<HTMLInputElement>} event
396
+ */
397
+ this.onWindowKeyDown = (event) => {
398
+ this.lastKeyDown = event.key;
399
+ // Avoid circular navigation
400
+ const activeElement = document.activeElement;
401
+ if ([KEYBOARD_KEYS.ArrowDown, KEYBOARD_KEYS.ArrowUp].includes(event.key) && activeElement) {
402
+ // Disable CircularNavigation
403
+ // There is property in focusZoneProps, but it is overwritten by fluent ui and we can not change it from outside
404
+ const positions = this.getEdgePosition(activeElement);
405
+ const fromFirst = positions.includes(EdgePosition.First) && event.key === KEYBOARD_KEYS.ArrowUp;
406
+ const fromLast = positions.includes(EdgePosition.Last) && event.key === KEYBOARD_KEYS.ArrowDown;
407
+ if (fromFirst || fromLast) {
408
+ // Circular navigation case.
409
+ // Check if first item focused in root menu.
410
+ if (fromFirst && activeElement.closest(`.${SELECTOR_CLASSES.callout}${this.state.uiidInput}`)) {
411
+ // Focus input field if navigation triggered from first item using ArrowUp
412
+ this.inputRef.current?.focus();
413
+ }
414
+ event.stopPropagation();
415
+ event.preventDefault();
416
+ }
417
+ }
418
+ };
419
+ /**
420
+ * Method handles focus logic if arrow key was pressed.
421
+ *
422
+ * @param {FocusEvent} event
423
+ */
424
+ this.handleCustomDownKey = (event) => {
425
+ if (this.lastKeyDown.includes('Arrow')) {
426
+ this.lastKeyDown = '';
427
+ this.onFocusElementChanged(event.target);
428
+ }
429
+ };
430
+ /**
431
+ * Method appends custom keydown and focus event listeners when context menu is opened.
432
+ */
433
+ this.applyCustomKeyDownHandlingEvents = () => {
434
+ window.addEventListener('keydown', this.onWindowKeyDown, true);
435
+ window.addEventListener('focus', this.handleCustomDownKey, true);
436
+ };
437
+ /**
438
+ * Method removes custom keydown and focus event listeners when context menu is dismissed.
439
+ */
440
+ this.removeCustomKeyDownHandlingEvents = () => {
441
+ window.removeEventListener('keydown', this.onWindowKeyDown, true);
442
+ window.removeEventListener('focus', this.handleCustomDownKey, true);
443
+ };
444
+ /**
445
+ * Method receives any menu child element and returns edge positions if item is first or last in rendered menu.
446
+ *
447
+ * @param {Element} itemElement Item's DOM to check position.
448
+ * @returns {EdgePosition[]} Returns positions if element is first or last in menu - also can be both.
449
+ */
450
+ this.getEdgePosition = (itemElement) => {
451
+ const container = itemElement.closest('ul');
452
+ const item = itemElement.closest('li');
453
+ const position = [];
454
+ if (container && item) {
455
+ if (container.children[0] === item) {
456
+ position.push(EdgePosition.First);
457
+ }
458
+ if (container.children[container.children.length - 1] === item) {
459
+ position.push(EdgePosition.Last);
460
+ }
461
+ }
462
+ return position;
463
+ };
464
+ /**
465
+ * Recursive method finds menu item info object in tree menu items by passed value/key of item.
466
+ *
467
+ * @param {string} [value] Value/key of item.
468
+ * @param {IContextualMenuItem[]} [items] Menu items.
469
+ * @param {TreeItemInfo} [parent] Item's parent object.
470
+ * @param {number} [level] Level of item in tree structure.
471
+ * @returns {TreeItemInfo | undefined} Found menu item.
472
+ */
473
+ this.findItemByValue = (value, items = [], parent, level = 0) => {
474
+ let selectedItem;
475
+ for (let i = 0; i < items.length; i++) {
476
+ const item = items[i];
477
+ const selectedItemTemp = {
478
+ item,
479
+ index: i,
480
+ parent,
481
+ level
482
+ };
483
+ if (item.value === value) {
484
+ selectedItem = selectedItemTemp;
485
+ }
486
+ else if (item.subMenuProps?.items?.length) {
487
+ selectedItem = this.findItemByValue(value, item.subMenuProps.items, selectedItemTemp, level + 1);
488
+ }
489
+ if (selectedItem) {
490
+ break;
491
+ }
492
+ }
493
+ return selectedItem;
494
+ };
495
+ /**
496
+ * Method finds DOM node of menu item based on received item object and container DOM.
497
+ *
498
+ * @param {HTMLElement} container Menu container DOM.
499
+ * @param {TreeItemInfo} item Menu item info object.
500
+ * @returns {HTMLElement | undefined} Found DOM element of item.
501
+ */
502
+ this.getItemTarget = (container, item) => {
503
+ let itemDom;
504
+ const listDom = container.querySelector('.ms-ContextualMenu-list');
505
+ if (listDom && listDom.childNodes[item.index]) {
506
+ const listItemDom = listDom.childNodes[item.index];
507
+ const itemElement = listItemDom.firstChild;
508
+ itemDom = itemElement;
509
+ }
510
+ return itemDom;
511
+ };
512
+ /**
513
+ * Method focuses context menu item based on recieved value/key and menude data(items and hoisted object).
514
+ * Method works with any level menu.
515
+ *
516
+ * @param {string} [value] Value/key of item.
517
+ * @param {IContextualMenuItem[]} [items] Target menu items.
518
+ */
519
+ this.focusItemWithValue = (value, items = []) => {
520
+ const selectedItem = this.findItemByValue(value, items);
521
+ const callout = this.getCalloutDomRef(!!this.defaultSubmenuFocus);
522
+ this.defaultSubmenuFocus = undefined;
523
+ if (selectedItem && callout) {
524
+ let itemDom;
525
+ if (selectedItem.parent) {
526
+ // Item is in next level of menu - we need open submenu
527
+ const parentItemDom = this.getItemTarget(callout, selectedItem.parent);
528
+ if (parentItemDom) {
529
+ this.defaultSubmenuFocus = selectedItem;
530
+ parentItemDom.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 39, which: 39, bubbles: true }));
531
+ }
532
+ }
533
+ else {
534
+ // Item is in first level
535
+ itemDom = this.getItemTarget(callout, selectedItem);
536
+ }
537
+ // Focus target item or focus container while submenu is not opened
538
+ if (itemDom) {
539
+ itemDom.focus();
540
+ }
541
+ else {
542
+ const menuContainer = callout.querySelector(`.${SELECTOR_CLASSES.scrollArea}`);
543
+ menuContainer?.focus();
544
+ }
545
+ }
546
+ };
547
+ /**
548
+ * Generate unique id for menu component references.
549
+ *
550
+ * @param {string} value Value of item.
551
+ * @param {number} level Level of item in tree structure.
552
+ * @returns {string} Id containing value andf level in format "${value}__${level}".
553
+ */
554
+ this.getRefId = (value, level) => {
555
+ return `${value}__${level}`;
556
+ };
557
+ this.state = {
558
+ query: '',
559
+ hasSelected: this.props.value ? true : false,
560
+ // value has to be set, otherwise react treats this as "uncontrolled" component
561
+ // and displays warnings when value is set later on
562
+ value: this.props.value || '',
563
+ isHidden: true,
564
+ originalItems: [],
565
+ items: [],
566
+ valueSeparator: this.props.valueSeparator || '.',
567
+ uiidInput: uuid_1.default.v4(),
568
+ isDisabled: this.props.items.length ? false : true,
569
+ isMenuOpen: false,
570
+ valueChanged: false
571
+ };
572
+ this.toggleMenu = this.toggleMenu.bind(this);
573
+ this.onWindowKeyDown = this.onWindowKeyDown.bind(this);
574
+ this.handleCustomDownKey = this.handleCustomDownKey.bind(this);
575
+ // Suppress icon warnings, as they are irrelevant
576
+ (0, react_2.setIconOptions)({ disableWarnings: true });
577
+ }
578
+ /**
579
+ * Method resets value of dropdown input to original value, which was stored after open.
580
+ */
581
+ resetValue() {
582
+ this.setState({
583
+ value: this.originalValue,
584
+ valueChanged: false
585
+ });
586
+ }
587
+ /**
588
+ * Method returns class names for wrapper element depending on props and component state.
589
+ *
590
+ * @returns {string} Class names of wrapper element.
591
+ */
592
+ getClassNames() {
593
+ let classNames = `ui-treeDropdown-wrapper ui-treeDropdown-wrapper-menu${this.state.isMenuOpen ? '-open' : '-close'} ui-treeDropdown-wrapper-${this.state.uiidInput}`;
594
+ if (this.state.isDisabled) {
595
+ classNames += ' disabled';
596
+ }
597
+ if (this.props.readOnly) {
598
+ classNames += ' readonly';
599
+ }
600
+ return classNames;
601
+ }
602
+ /**
603
+ * @returns {JSX.Element}
604
+ */
605
+ render() {
606
+ const messageInfo = (0, ValidationMessage_1.getMessageInfo)(this.props);
607
+ let useTargetWidth = true;
608
+ if (this.props.useTargetWidth) {
609
+ useTargetWidth = false;
610
+ }
611
+ return (react_1.default.createElement("div", { className: `ui-treeDropdown ui-treeDropDown-${this.state.uiidInput} ${this.props.label ? 'ui-treeDropdown-with-label' : ''}` },
612
+ this.props.label && (react_1.default.createElement("label", { className: `${this.props.required ? 'required' : ''} ${this.state.isDisabled ? 'disabled' : ''}` }, this.props.label)),
613
+ react_1.default.createElement("div", { className: this.getClassNames() },
614
+ react_1.default.createElement(UIInput_1.UITextInput, { componentRef: this.inputRef, disabled: this.state.isDisabled, readOnly: this.props.readOnly, autoComplete: "off", value: this.state.value, placeholder: this.props.placeholderText, onKeyDown: this.handleKeyPress, onChange: this.handleOnChangeValue, onClick: () => {
615
+ this.toggleMenu(false);
616
+ }, onFocus: (event) => {
617
+ // Select the text of the input
618
+ event.target.select();
619
+ }, errorMessage: messageInfo.message }),
620
+ react_1.default.createElement(UIIconButton_1.UIIconButton, { tabIndex: -1, allowDisabledFocus: true, className: "ui-treeDropdown-caret", iconProps: { iconName: Icons_1.UiIcons.ArrowDown }, onClick: () => {
621
+ if (this.state.isHidden) {
622
+ // Menu would become visible - focus input
623
+ this.inputRef.current?.focus();
624
+ }
625
+ this.toggleMenu(!this.state.isHidden);
626
+ } })),
627
+ !this.state.isHidden && (react_1.default.createElement(UIContextualMenu_1.UIContextualMenu, { componentRef: this.UITreeDropdownRef, onRenderMenuList: this.handleRenderMenuList, className: "ui-treeDropDown-context-menu", target: `.ui-treeDropDown-${this.state.uiidInput}`, onMenuOpened: () => {
628
+ this.originalValue = this.state.value;
629
+ this.applyCustomKeyDownHandlingEvents();
630
+ this.setState({
631
+ isMenuOpen: true
632
+ });
633
+ }, onMenuDismissed: () => {
634
+ this.removeCustomKeyDownHandlingEvents();
635
+ this.setState({ isMenuOpen: false });
636
+ if (this.state.valueChanged) {
637
+ this.handleSelection(this.state.value ? this.state.value : '');
638
+ }
639
+ }, useTargetWidth: useTargetWidth, useTargetAsMinWidth: true, onRestoreFocus: (params) => {
640
+ params.originalElement?.focus();
641
+ }, shouldUpdateWhenHidden: true, items: this.state.items, onDismiss: this.handleDismiss, shouldFocusOnContainer: false, focusZoneProps: {
642
+ componentRef: this.UITreeDropdownFocusZoneRef,
643
+ handleTabKey: react_2.FocusZoneTabbableElements.none,
644
+ isCircularNavigation: false
645
+ }, shouldFocusOnMount: false, directionalHint: this.props.directionalHint, calloutProps: {
646
+ layerProps: {
647
+ className: `${SELECTOR_CLASSES.callout}${this.state.uiidInput}`
648
+ },
649
+ onLayerMounted: () => {
650
+ this.calculateSubmenuOffset();
651
+ }
652
+ }, styles: {
653
+ container: {
654
+ maxHeight: 192,
655
+ overflowY: 'auto'
656
+ }
657
+ }, maxWidth: this.props.maxWidth }))));
658
+ }
659
+ }
660
+ exports.UITreeDropdown = UITreeDropdown;
661
661
  //# sourceMappingURL=UITreeDropdown.js.map