cx 26.2.1 → 26.2.3

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 (324) hide show
  1. package/build/charts/Chart.d.ts.map +1 -1
  2. package/build/charts/Chart.js +5 -4
  3. package/build/charts/ColumnBarBase.d.ts +3 -3
  4. package/build/charts/ColumnBarBase.d.ts.map +1 -1
  5. package/build/charts/ColumnBarBase.js +1 -1
  6. package/build/charts/Legend.d.ts.map +1 -1
  7. package/build/charts/Legend.js +11 -4
  8. package/build/charts/Marker.d.ts +3 -3
  9. package/build/charts/Marker.d.ts.map +1 -1
  10. package/build/charts/MarkerLine.d.ts +7 -7
  11. package/build/charts/MarkerLine.d.ts.map +1 -1
  12. package/build/charts/MarkerLine.js +14 -10
  13. package/build/charts/PieChart.d.ts +4 -4
  14. package/build/charts/PieChart.d.ts.map +1 -1
  15. package/build/charts/PieChart.js +36 -14
  16. package/build/charts/PieLabel.d.ts.map +1 -1
  17. package/build/charts/PieLabel.js +2 -1
  18. package/build/charts/RangeMarker.d.ts +3 -3
  19. package/build/charts/RangeMarker.d.ts.map +1 -1
  20. package/build/charts/RangeMarker.js +1 -1
  21. package/build/charts/axis/TimeAxis.d.ts +3 -3
  22. package/build/charts/axis/TimeAxis.d.ts.map +1 -1
  23. package/build/charts/axis/TimeAxis.js +70 -21
  24. package/build/charts/helpers/ValueAtFinder.d.ts +1 -1
  25. package/build/charts/helpers/ValueAtFinder.d.ts.map +1 -1
  26. package/build/charts/helpers/ValueAtFinder.js +5 -2
  27. package/build/data/StructuredSelector.js +3 -4
  28. package/build/data/createAccessorModelProxy.d.ts +6 -11
  29. package/build/data/createAccessorModelProxy.d.ts.map +1 -1
  30. package/build/data/createAccessorModelProxy.js +1 -3
  31. package/build/svg/Ellipse.d.ts +5 -4
  32. package/build/svg/Ellipse.d.ts.map +1 -1
  33. package/build/svg/Ellipse.js +9 -6
  34. package/build/svg/Line.d.ts +1 -0
  35. package/build/svg/Line.d.ts.map +1 -1
  36. package/build/svg/Line.js +4 -1
  37. package/build/svg/Text.d.ts +12 -6
  38. package/build/svg/Text.d.ts.map +1 -1
  39. package/build/svg/Text.js +12 -4
  40. package/build/ui/Controller.d.ts +2 -0
  41. package/build/ui/Controller.d.ts.map +1 -1
  42. package/build/ui/Controller.js +3 -0
  43. package/build/ui/HoverSync.d.ts.map +1 -1
  44. package/build/ui/HoverSync.js +7 -2
  45. package/build/ui/Prop.d.ts +1 -1
  46. package/build/ui/Prop.d.ts.map +1 -1
  47. package/build/ui/Text.d.ts +3 -3
  48. package/build/ui/Text.d.ts.map +1 -1
  49. package/build/ui/Text.js +5 -5
  50. package/build/ui/adapter/GroupAdapter.d.ts.map +1 -1
  51. package/build/ui/adapter/GroupAdapter.js +20 -10
  52. package/build/ui/app/History.js +1 -1
  53. package/build/widgets/List.d.ts.map +1 -1
  54. package/build/widgets/List.js +6 -7
  55. package/build/widgets/drag-drop/DropZone.d.ts +3 -3
  56. package/build/widgets/drag-drop/DropZone.d.ts.map +1 -1
  57. package/build/widgets/form/Calendar.d.ts.map +1 -1
  58. package/build/widgets/form/Calendar.js +30 -11
  59. package/build/widgets/form/ColorField.d.ts.map +1 -1
  60. package/build/widgets/form/ColorField.js +16 -7
  61. package/build/widgets/form/DateTimeField.d.ts.map +1 -1
  62. package/build/widgets/form/DateTimeField.js +23 -10
  63. package/build/widgets/form/Field.d.ts +2 -0
  64. package/build/widgets/form/Field.d.ts.map +1 -1
  65. package/build/widgets/form/Field.js +11 -5
  66. package/build/widgets/form/LookupField.d.ts +1 -1
  67. package/build/widgets/form/LookupField.d.ts.map +1 -1
  68. package/build/widgets/form/LookupField.js +6 -6
  69. package/build/widgets/form/MonthField.d.ts.map +1 -1
  70. package/build/widgets/form/MonthField.js +15 -7
  71. package/build/widgets/form/MonthPicker.d.ts +1 -2
  72. package/build/widgets/form/MonthPicker.d.ts.map +1 -1
  73. package/build/widgets/form/MonthPicker.js +84 -41
  74. package/build/widgets/form/NumberField.d.ts +2 -0
  75. package/build/widgets/form/NumberField.d.ts.map +1 -1
  76. package/build/widgets/form/NumberField.js +45 -15
  77. package/build/widgets/form/TextField.d.ts +1 -9
  78. package/build/widgets/form/TextField.d.ts.map +1 -1
  79. package/build/widgets/form/TextField.js +1 -1
  80. package/build/widgets/grid/Grid.d.ts +2 -2
  81. package/build/widgets/grid/Grid.d.ts.map +1 -1
  82. package/build/widgets/grid/Grid.js +9 -4
  83. package/build/widgets/grid/Pagination.d.ts.map +1 -1
  84. package/build/widgets/grid/Pagination.js +4 -4
  85. package/build/widgets/grid/TreeNode.d.ts.map +1 -1
  86. package/build/widgets/grid/TreeNode.js +10 -2
  87. package/build/widgets/icons/folder.d.ts.map +1 -1
  88. package/build/widgets/icons/folder.js +1 -0
  89. package/build/widgets/icons/forward.d.ts.map +1 -1
  90. package/build/widgets/icons/forward.js +4 -3
  91. package/build/widgets/icons/loading.d.ts.map +1 -1
  92. package/build/widgets/icons/loading.js +6 -5
  93. package/build/widgets/icons/square.d.ts.map +1 -1
  94. package/build/widgets/icons/square.js +3 -3
  95. package/build/widgets/index.d.ts +3 -1
  96. package/build/widgets/index.d.ts.map +1 -1
  97. package/build/widgets/index.js +3 -1
  98. package/build/widgets/overlay/ContextMenu.d.ts.map +1 -1
  99. package/build/widgets/overlay/ContextMenu.js +2 -0
  100. package/build/widgets/overlay/Dropdown.d.ts +2 -1
  101. package/build/widgets/overlay/Dropdown.d.ts.map +1 -1
  102. package/build/widgets/overlay/Dropdown.js +75 -20
  103. package/build/widgets/overlay/MsgBox.d.ts +1 -0
  104. package/build/widgets/overlay/MsgBox.d.ts.map +1 -1
  105. package/build/widgets/overlay/MsgBox.js +2 -2
  106. package/build/widgets/overlay/Overlay.d.ts +32 -2
  107. package/build/widgets/overlay/Overlay.d.ts.map +1 -1
  108. package/build/widgets/overlay/Overlay.js +47 -16
  109. package/build/widgets/overlay/Toast.d.ts +1 -1
  110. package/build/widgets/overlay/Toast.d.ts.map +1 -1
  111. package/build/widgets/overlay/Toast.js +4 -1
  112. package/build/widgets/overlay/Tooltip.d.ts +6 -0
  113. package/build/widgets/overlay/Tooltip.d.ts.map +1 -1
  114. package/build/widgets/overlay/Tooltip.js +24 -9
  115. package/build/widgets/overlay/Window.d.ts.map +1 -1
  116. package/build/widgets/overlay/Window.js +24 -9
  117. package/dist/charts.css +325 -272
  118. package/dist/charts.js +11 -5
  119. package/dist/data.js +2 -2
  120. package/dist/manifest.js +892 -880
  121. package/dist/svg.css +14 -8
  122. package/dist/svg.js +9 -1
  123. package/dist/ui.js +29 -16
  124. package/dist/widgets.css +997 -294
  125. package/dist/widgets.js +317 -121
  126. package/package.json +1 -1
  127. package/src/charts/Bar.scss +13 -10
  128. package/src/charts/BarGraph.scss +31 -29
  129. package/src/charts/BubbleGraph.scss +11 -8
  130. package/src/charts/Chart.ts +5 -3
  131. package/src/charts/Column.scss +13 -10
  132. package/src/charts/ColumnBarBase.tsx +255 -230
  133. package/src/charts/ColumnGraph.scss +13 -11
  134. package/src/charts/Gridlines.scss +10 -8
  135. package/src/charts/Legend.scss +57 -50
  136. package/src/charts/Legend.tsx +257 -213
  137. package/src/charts/LegendEntry.scss +35 -29
  138. package/src/charts/LineGraph.scss +28 -25
  139. package/src/charts/Marker.scss +12 -10
  140. package/src/charts/Marker.tsx +3 -2
  141. package/src/charts/MarkerLine.scss +11 -8
  142. package/src/charts/MarkerLine.tsx +196 -177
  143. package/src/charts/PieChart.scss +12 -9
  144. package/src/charts/PieChart.tsx +717 -591
  145. package/src/charts/PieLabel.tsx +99 -81
  146. package/src/charts/Range.scss +11 -8
  147. package/src/charts/RangeMarker.tsx +204 -187
  148. package/src/charts/ScatterGraph.scss +12 -9
  149. package/src/charts/axis/Axis.scss +6 -5
  150. package/src/charts/axis/CategoryAxis.scss +10 -8
  151. package/src/charts/axis/NumericAxis.scss +9 -6
  152. package/src/charts/axis/TimeAxis.scss +9 -6
  153. package/src/charts/axis/TimeAxis.tsx +753 -637
  154. package/src/charts/axis/index.scss +4 -5
  155. package/src/charts/axis/variables.scss +4 -2
  156. package/src/charts/helpers/ValueAtFinder.ts +19 -5
  157. package/src/charts/index.scss +16 -19
  158. package/src/charts/maps.scss +0 -0
  159. package/src/charts/palette.scss +11 -31
  160. package/src/charts/palette.variables.scss +23 -0
  161. package/src/charts/variables.scss +35 -3
  162. package/src/data/StructuredSelector.ts +2 -2
  163. package/src/data/createAccessorModelProxy.ts +66 -74
  164. package/src/index.scss +5 -6
  165. package/src/maps.scss +5 -0
  166. package/src/svg/Ellipse.tsx +62 -55
  167. package/src/svg/Line.tsx +57 -42
  168. package/src/svg/Svg.scss +6 -6
  169. package/src/svg/Text.scss +19 -0
  170. package/src/svg/Text.tsx +172 -116
  171. package/src/svg/index.scss +3 -2
  172. package/src/svg/maps.scss +0 -0
  173. package/src/svg/variables.scss +0 -0
  174. package/src/ui/Container.spec.ts +59 -0
  175. package/src/ui/Controller.spec.tsx +30 -0
  176. package/src/ui/Controller.ts +5 -0
  177. package/src/ui/HoverSync.tsx +179 -147
  178. package/src/ui/Prop.ts +1 -1
  179. package/src/ui/Text.ts +12 -9
  180. package/src/ui/adapter/GroupAdapter.spec.ts +42 -0
  181. package/src/ui/adapter/GroupAdapter.ts +25 -14
  182. package/src/ui/app/History.ts +1 -1
  183. package/src/ui/index.scss +1 -1
  184. package/src/ui/layout/LabelsLeftLayout.scss +5 -7
  185. package/src/ui/layout/LabelsTopLayout.scss +4 -6
  186. package/src/ui/layout/index.scss +2 -3
  187. package/src/ui/maps.scss +0 -0
  188. package/src/ui/variables.scss +1 -2
  189. package/src/util/index.scss +4 -2
  190. package/src/util/maps.scss +1 -0
  191. package/src/util/scss/besm.scss +15 -0
  192. package/src/util/scss/calc.scss +103 -11
  193. package/src/util/scss/defaults.scss +24 -0
  194. package/src/util/scss/elements.scss +78 -0
  195. package/src/util/scss/global.scss +15 -0
  196. package/src/util/scss/include.scss +17 -9
  197. package/src/util/scss/index.scss +1 -9
  198. package/src/util/scss/maps.scss +2 -0
  199. package/src/util/scss/pad-size.scss +9 -0
  200. package/src/util/scss/padding.scss +6 -0
  201. package/src/util/scss/screen-size.scss +5 -0
  202. package/src/util/scss/variables.scss +6 -0
  203. package/src/util/variables.scss +1 -0
  204. package/src/variables.scss +5 -217
  205. package/src/widgets/Button.maps.scss +103 -0
  206. package/src/widgets/Button.scss +33 -9
  207. package/src/widgets/Button.variables.scss +8 -104
  208. package/src/widgets/CxCredit.scss +2 -0
  209. package/src/widgets/FlexBox.scss +16 -11
  210. package/src/widgets/Heading.scss +6 -0
  211. package/src/widgets/HighlightedSearchText.scss +8 -1
  212. package/src/widgets/Icon.scss +6 -0
  213. package/src/widgets/List.scss +7 -0
  214. package/src/widgets/List.tsx +6 -7
  215. package/src/widgets/ProgressBar.scss +9 -0
  216. package/src/widgets/Resizer.scss +9 -7
  217. package/src/widgets/Section.scss +53 -56
  218. package/src/widgets/animations.scss +4 -2
  219. package/src/widgets/box.scss +47 -0
  220. package/src/widgets/drag-drop/DragClone.scss +12 -4
  221. package/src/widgets/drag-drop/DragHandle.scss +12 -6
  222. package/src/widgets/drag-drop/DragSource.scss +12 -6
  223. package/src/widgets/drag-drop/DropZone.scss +9 -0
  224. package/src/widgets/drag-drop/DropZone.tsx +3 -3
  225. package/src/widgets/drag-drop/index.scss +4 -4
  226. package/src/widgets/drag-drop/maps.scss +7 -0
  227. package/src/widgets/drag-drop/variables.scss +8 -5
  228. package/src/widgets/form/Calendar.maps.scss +54 -0
  229. package/src/widgets/form/Calendar.scss +49 -11
  230. package/src/widgets/form/Calendar.tsx +755 -653
  231. package/src/widgets/form/Calendar.variables.scss +3 -46
  232. package/src/widgets/form/Checkbox.maps.scss +34 -0
  233. package/src/widgets/form/Checkbox.scss +14 -3
  234. package/src/widgets/form/Checkbox.variables.scss +4 -36
  235. package/src/widgets/form/ColorField.scss +21 -2
  236. package/src/widgets/form/ColorField.tsx +485 -431
  237. package/src/widgets/form/ColorPicker.maps.scss +21 -0
  238. package/src/widgets/form/ColorPicker.scss +26 -9
  239. package/src/widgets/form/ColorPicker.variables.scss +3 -16
  240. package/src/widgets/form/DateTimeField.scss +54 -21
  241. package/src/widgets/form/DateTimeField.tsx +697 -615
  242. package/src/widgets/form/DateTimePicker.scss +14 -4
  243. package/src/widgets/form/Field.maps.scss +122 -0
  244. package/src/widgets/form/Field.scss +54 -18
  245. package/src/widgets/form/Field.tsx +611 -504
  246. package/src/widgets/form/Field.variables.scss +46 -0
  247. package/src/widgets/form/HelpText.scss +8 -5
  248. package/src/widgets/form/Label.scss +10 -3
  249. package/src/widgets/form/LookupField.maps.scss +26 -0
  250. package/src/widgets/form/LookupField.scss +54 -24
  251. package/src/widgets/form/LookupField.tsx +25 -21
  252. package/src/widgets/form/MonthField.scss +48 -26
  253. package/src/widgets/form/MonthField.tsx +645 -567
  254. package/src/widgets/form/MonthPicker.maps.scss +50 -0
  255. package/src/widgets/form/MonthPicker.scss +44 -35
  256. package/src/widgets/form/MonthPicker.tsx +954 -724
  257. package/src/widgets/form/MonthPicker.variables.scss +24 -0
  258. package/src/widgets/form/NumberField.scss +19 -2
  259. package/src/widgets/form/NumberField.tsx +576 -466
  260. package/src/widgets/form/Radio.maps.scss +36 -0
  261. package/src/widgets/form/Radio.scss +12 -2
  262. package/src/widgets/form/Radio.variables.scss +3 -42
  263. package/src/widgets/form/Select.scss +25 -9
  264. package/src/widgets/form/Slider.scss +23 -14
  265. package/src/widgets/form/Switch.scss +18 -8
  266. package/src/widgets/form/TextArea.scss +14 -1
  267. package/src/widgets/form/TextField.scss +24 -3
  268. package/src/widgets/form/TextField.tsx +9 -21
  269. package/src/widgets/form/UploadButton.scss +14 -6
  270. package/src/widgets/form/ValidationError.scss +10 -6
  271. package/src/widgets/form/Wheel.scss +14 -4
  272. package/src/widgets/form/index.scss +22 -24
  273. package/src/widgets/form/maps.scss +81 -0
  274. package/src/widgets/form/variables.scss +111 -355
  275. package/src/widgets/grid/Grid.scss +19 -2
  276. package/src/widgets/grid/Grid.spec.ts +42 -0
  277. package/src/widgets/grid/Grid.tsx +15 -7
  278. package/src/widgets/grid/Pagination.scss +11 -2
  279. package/src/widgets/grid/Pagination.tsx +110 -102
  280. package/src/widgets/grid/TreeNode.scss +25 -8
  281. package/src/widgets/grid/TreeNode.tsx +127 -116
  282. package/src/widgets/grid/index.scss +3 -4
  283. package/src/widgets/grid/maps.scss +110 -0
  284. package/src/widgets/grid/variables.scss +48 -137
  285. package/src/widgets/icons/folder.tsx +1 -2
  286. package/src/widgets/icons/forward.tsx +23 -20
  287. package/src/widgets/icons/loading.tsx +22 -19
  288. package/src/widgets/icons/square.tsx +20 -17
  289. package/src/widgets/index.scss +16 -16
  290. package/src/widgets/index.ts +63 -58
  291. package/src/widgets/lists.scss +42 -0
  292. package/src/widgets/maps.scss +139 -0
  293. package/src/widgets/nav/Link.scss +14 -1
  294. package/src/widgets/nav/Menu.scss +13 -7
  295. package/src/widgets/nav/Menu.variables.scss +1 -12
  296. package/src/widgets/nav/MenuItem.scss +21 -6
  297. package/src/widgets/nav/Scroller.scss +11 -2
  298. package/src/widgets/nav/Tab.maps.scss +78 -0
  299. package/src/widgets/nav/Tab.scss +12 -6
  300. package/src/widgets/nav/Tab.variables.scss +7 -76
  301. package/src/widgets/nav/cover.scss +6 -4
  302. package/src/widgets/nav/index.scss +6 -6
  303. package/src/widgets/nav/maps.scss +32 -0
  304. package/src/widgets/nav/variables.scss +4 -11
  305. package/src/widgets/overlay/ContextMenu.ts +3 -0
  306. package/src/widgets/overlay/Dropdown.scss +47 -16
  307. package/src/widgets/overlay/Dropdown.tsx +851 -676
  308. package/src/widgets/overlay/MsgBox.tsx +125 -111
  309. package/src/widgets/overlay/Overlay.scss +60 -40
  310. package/src/widgets/overlay/Overlay.tsx +948 -800
  311. package/src/widgets/overlay/Toast.scss +42 -34
  312. package/src/widgets/overlay/Toast.ts +11 -1
  313. package/src/widgets/overlay/Tooltip.scss +27 -96
  314. package/src/widgets/overlay/Tooltip.tsx +376 -309
  315. package/src/widgets/overlay/Window.maps.scss +51 -0
  316. package/src/widgets/overlay/Window.scss +17 -17
  317. package/src/widgets/overlay/Window.tsx +291 -236
  318. package/src/widgets/overlay/Window.variables.scss +2 -43
  319. package/src/widgets/overlay/index.d.ts +11 -11
  320. package/src/widgets/overlay/index.scss +6 -15
  321. package/src/widgets/overlay/maps.scss +44 -0
  322. package/src/widgets/overlay/variables.scss +11 -42
  323. package/src/widgets/variables.scss +33 -117
  324. package/src/global.scss +0 -14
@@ -6,7 +6,13 @@ import { calculateNaturalElementHeight } from "../../util/calculateNaturalElemen
6
6
  import { closestParent, findFirst, isFocusable } from "../../util/DOM";
7
7
  import { getTopLevelBoundingClientRect } from "../../util/getTopLevelBoundingClientRect";
8
8
  import { isTouchDevice } from "../../util/isTouchDevice";
9
- import { Overlay, OverlayBase, OverlayConfig, OverlayInstance } from "./Overlay";
9
+ import {
10
+ ConfigureOverlayContainerContext,
11
+ Overlay,
12
+ OverlayBase,
13
+ OverlayConfig,
14
+ OverlayInstance,
15
+ } from "./Overlay";
10
16
  import { Instance } from "../../ui/Instance";
11
17
  import { StringProp } from "../../ui/Prop";
12
18
  import { RenderingContext } from "../../ui/RenderingContext";
@@ -19,714 +25,883 @@ import { HtmlElement } from "../HtmlElement";
19
25
  */
20
26
 
21
27
  export interface DropdownConfig extends OverlayConfig {
22
- /** Placement option for the dropdown relative to the trigger element. */
23
- placement?: StringProp | null;
28
+ /** Placement option for the dropdown relative to the trigger element. */
29
+ placement?: StringProp | null;
24
30
 
25
- /** Offset distance from the trigger element. */
26
- offset?: number;
31
+ /** Offset distance from the trigger element. */
32
+ offset?: number;
27
33
 
28
- /** Match the dropdown width to the trigger element. */
29
- matchWidth?: boolean;
34
+ /** Match the dropdown width to the trigger element. */
35
+ matchWidth?: boolean;
30
36
 
31
- /** Match the dropdown max-width to the trigger element. */
32
- matchMaxWidth?: boolean;
37
+ /** Match the dropdown max-width to the trigger element. */
38
+ matchMaxWidth?: boolean;
33
39
 
34
- /** Placement preference order. */
35
- placementOrder?: string;
40
+ /** Placement preference order. */
41
+ placementOrder?: string;
36
42
 
37
- /** Constrain the dropdown within the viewport. */
38
- constrain?: boolean;
43
+ /** Constrain the dropdown within the viewport. */
44
+ constrain?: boolean;
39
45
 
40
- /** Positioning strategy - "fixed", "absolute", or "auto". */
41
- positioning?: string;
46
+ /** Positioning strategy - "fixed", "absolute", or "auto". */
47
+ positioning?: string;
42
48
 
43
- /** Use touch-friendly positioning on touch devices. */
44
- touchFriendly?: boolean;
49
+ /** Use touch-friendly positioning on touch devices. */
50
+ touchFriendly?: boolean;
45
51
 
46
- /** Show an arrow pointing to the trigger element. */
47
- arrow?: boolean;
52
+ /** Show an arrow pointing to the trigger element. */
53
+ arrow?: boolean;
48
54
 
49
- /** Add padding around the dropdown. */
50
- pad?: boolean;
55
+ /** Add padding around the dropdown. */
56
+ pad?: boolean;
51
57
 
52
- /** Element explosion distance for positioning. */
53
- elementExplode?: number;
58
+ /** Element explosion distance for positioning. */
59
+ elementExplode?: number;
54
60
 
55
- /** Padding from screen edges. */
56
- screenPadding?: number;
61
+ /** Padding from screen edges. */
62
+ screenPadding?: number;
57
63
 
58
- /** First child element defines the height. */
59
- firstChildDefinesHeight?: boolean;
64
+ /** First child element defines the height. */
65
+ firstChildDefinesHeight?: boolean;
60
66
 
61
- /** First child element defines the width. */
62
- firstChildDefinesWidth?: boolean;
67
+ /** First child element defines the width. */
68
+ firstChildDefinesWidth?: boolean;
63
69
 
64
- /** The dropdown will be automatically closed if the page is scrolled a certain distance. */
65
- closeOnScrollDistance?: number;
70
+ /** The dropdown will be automatically closed if the page is scrolled a certain distance. */
71
+ closeOnScrollDistance?: number;
66
72
 
67
- /** The element to position the dropdown relative to. */
68
- relatedElement?: Element;
73
+ /** The element to position the dropdown relative to. */
74
+ relatedElement?: Element;
69
75
 
70
- /** Callback to resolve the related element. */
71
- onResolveRelatedElement?: string | ((beaconEl: Element, instance: any) => Element);
76
+ /** Callback to resolve the related element. */
77
+ onResolveRelatedElement?:
78
+ | string
79
+ | ((beaconEl: Element, instance: any) => Element);
72
80
 
73
- /** Callback to measure natural content size. */
74
- onMeasureNaturalContentSize?: string | ((el: Element, instance: any) => { width?: number; height?: number });
81
+ /** Callback to measure natural content size. */
82
+ onMeasureNaturalContentSize?:
83
+ | string
84
+ | ((el: Element, instance: any) => { width?: number; height?: number });
75
85
 
76
- /** Callback when dropdown mounts. */
77
- onDropdownDidMount?: string;
86
+ /** Callback when dropdown mounts. */
87
+ onDropdownDidMount?: string;
78
88
 
79
- /** Callback to validate dropdown position. */
80
- pipeValidateDropdownPosition?: string;
89
+ /** Callback to validate dropdown position. */
90
+ pipeValidateDropdownPosition?: string;
81
91
 
82
- /** Callback when dropdown is dismissed after scroll. */
83
- onDismissAfterScroll?: string;
92
+ /** Callback when dropdown is dismissed after scroll. */
93
+ onDismissAfterScroll?: string;
84
94
 
85
- /** Track mouse position for dropdowns. */
86
- trackMouse?: boolean;
95
+ /** Track mouse position for dropdowns. */
96
+ trackMouse?: boolean;
87
97
 
88
- /** Track mouse X position. */
89
- trackMouseX?: boolean;
98
+ /** Track mouse X position. */
99
+ trackMouseX?: boolean;
90
100
 
91
- /** Track mouse Y position. */
92
- trackMouseY?: boolean;
101
+ /** Track mouse Y position. */
102
+ trackMouseY?: boolean;
93
103
 
94
- /** Cover the related element with dropdown. */
95
- cover?: boolean;
104
+ /** Cover the related element with dropdown. */
105
+ cover?: boolean;
96
106
  }
97
107
 
98
108
  export class DropdownInstance<
99
- WidgetType extends DropdownBase<any, any> = Dropdown,
109
+ WidgetType extends DropdownBase<any, any> = Dropdown,
100
110
  > extends OverlayInstance<WidgetType> {
101
- mousePosition?: any;
102
- parentPositionChangeEvent?: any;
103
- initialScreenPosition?: any;
104
- relatedElement?: HTMLElement;
105
- needsBeacon?: boolean;
111
+ mousePosition?: any;
112
+ parentPositionChangeEvent?: any;
113
+ initialScreenPosition?: any;
114
+ relatedElement?: HTMLElement;
115
+ needsBeacon?: boolean;
106
116
  }
107
117
 
108
118
  export class DropdownBase<
109
- Config extends DropdownConfig = DropdownConfig,
110
- InstanceType extends DropdownInstance<any> = DropdownInstance<any>,
119
+ Config extends DropdownConfig = DropdownConfig,
120
+ InstanceType extends DropdownInstance<any> = DropdownInstance<any>,
111
121
  > extends OverlayBase<Config, InstanceType> {
112
- declare trackMouse?: boolean;
113
- declare trackMouseX?: boolean;
114
- declare trackMouseY?: boolean;
115
- declare offset: number;
116
- declare matchWidth?: boolean;
117
- declare matchMaxWidth?: boolean;
118
- declare placementOrder: string;
119
- declare placement?: StringProp | null;
120
- declare constrain?: boolean;
121
- declare positioning?: string;
122
- declare touchFriendly?: boolean;
123
- declare arrow?: boolean;
124
- declare elementExplode?: number;
125
- declare screenPadding: number;
126
- declare firstChildDefinesHeight?: boolean;
127
- declare firstChildDefinesWidth?: boolean;
128
- declare closeOnScrollDistance: number;
129
- declare relatedElement?: HTMLElement;
130
- declare onResolveRelatedElement?: string | ((beaconEl: Element, instance: any) => Element);
131
- declare onMeasureNaturalContentSize?: string | ((el: Element, instance: any) => { width?: number; height?: number });
132
- declare onDropdownDidMount?: string;
133
- declare pipeValidateDropdownPosition?: string;
134
- declare onDismissAfterScroll?: string;
135
- declare onKeyDown?: string;
136
- declare cover?: boolean;
137
- declare mousePosition?: any;
138
- declare mouseTrap?: boolean;
139
- declare createDelay?: number;
140
-
141
- init() {
142
- if (this.trackMouse) {
143
- this.trackMouseX = true;
144
- this.trackMouseY = true;
145
- }
146
- if (this.autoFocus && !this.hasOwnProperty("focusable")) this.focusable = true;
147
- super.init();
148
- }
149
-
150
- declareData(...args: any[]) {
151
- return super.declareData(...args, {
152
- placement: undefined,
153
- });
154
- }
155
-
156
- initInstance(context: RenderingContext, instance: InstanceType): void {
157
- instance.mousePosition = this.mousePosition;
158
- instance.parentPositionChangeEvent = context.parentPositionChangeEvent;
159
- super.initInstance(context, instance);
160
- }
161
-
162
- explore(context: RenderingContext, instance: InstanceType): void {
163
- context.push("lastDropdown", instance);
164
- super.explore(context, instance);
165
- }
166
-
167
- exploreCleanup(context: RenderingContext, instance: InstanceType): void {
168
- context.pop("lastDropdown");
169
- super.exploreCleanup(context, instance);
170
- }
171
-
172
- overlayDidMount(instance: InstanceType, component: any): void {
173
- super.overlayDidMount(instance, component);
174
- var scrollableParents: Element[] = (component.scrollableParents = [window as any]);
175
- component.updateDropdownPosition = (e: any) => this.updateDropdownPosition(instance, component);
176
-
177
- instance.initialScreenPosition = null;
178
-
179
- var el = instance.relatedElement?.parentElement;
180
- while (el) {
181
- scrollableParents.push(el);
182
- el = el.parentElement;
183
- }
184
- scrollableParents.forEach((el: any) => {
185
- el.addEventListener("scroll", component.updateDropdownPosition);
186
- });
187
- component.offResize = ResizeManager.subscribe(component.updateDropdownPosition);
188
-
189
- if (this.onDropdownDidMount) instance.invoke("onDropdownDidMount", instance, component);
190
-
191
- if (this.pipeValidateDropdownPosition)
192
- instance.invoke("pipeValidateDropdownPosition", component.updateDropdownPosition, instance);
193
-
194
- if (instance.parentPositionChangeEvent)
195
- component.offParentPositionChange = instance.parentPositionChangeEvent.subscribe(
196
- component.updateDropdownPosition,
197
- );
198
- }
199
-
200
- overlayDidUpdate(instance: InstanceType, component: any): void {
122
+ declare trackMouse?: boolean;
123
+ declare trackMouseX?: boolean;
124
+ declare trackMouseY?: boolean;
125
+ declare offset: number;
126
+ declare matchWidth?: boolean;
127
+ declare matchMaxWidth?: boolean;
128
+ declare placementOrder: string;
129
+ declare placement?: StringProp | null;
130
+ declare constrain?: boolean;
131
+ declare positioning?: string;
132
+ declare touchFriendly?: boolean;
133
+ declare arrow?: boolean;
134
+ declare elementExplode?: number;
135
+ declare screenPadding: number;
136
+ declare firstChildDefinesHeight?: boolean;
137
+ declare firstChildDefinesWidth?: boolean;
138
+ declare closeOnScrollDistance: number;
139
+ declare relatedElement?: HTMLElement;
140
+ declare onResolveRelatedElement?:
141
+ | string
142
+ | ((beaconEl: Element, instance: any) => Element);
143
+ declare onMeasureNaturalContentSize?:
144
+ | string
145
+ | ((el: Element, instance: any) => { width?: number; height?: number });
146
+ declare onDropdownDidMount?: string;
147
+ declare pipeValidateDropdownPosition?: string;
148
+ declare onDismissAfterScroll?: string;
149
+ declare onKeyDown?: string;
150
+ declare cover?: boolean;
151
+ declare mousePosition?: any;
152
+ declare mouseTrap?: boolean;
153
+ declare createDelay?: number;
154
+
155
+ init() {
156
+ if (this.trackMouse) {
157
+ this.trackMouseX = true;
158
+ this.trackMouseY = true;
159
+ }
160
+ if (this.autoFocus && !this.hasOwnProperty("focusable"))
161
+ this.focusable = true;
162
+ super.init();
163
+ }
164
+
165
+ declareData(...args: any[]) {
166
+ return super.declareData(...args, {
167
+ placement: undefined,
168
+ });
169
+ }
170
+
171
+ initInstance(context: RenderingContext, instance: InstanceType): void {
172
+ instance.mousePosition = this.mousePosition;
173
+ instance.parentPositionChangeEvent = context.parentPositionChangeEvent;
174
+ super.initInstance(context, instance);
175
+ }
176
+
177
+ explore(context: RenderingContext, instance: InstanceType): void {
178
+ context.push("lastDropdown", instance);
179
+ super.explore(context, instance);
180
+ }
181
+
182
+ exploreCleanup(context: RenderingContext, instance: InstanceType): void {
183
+ context.pop("lastDropdown");
184
+ super.exploreCleanup(context, instance);
185
+ }
186
+
187
+ overlayDidMount(instance: InstanceType, component: any): void {
188
+ super.overlayDidMount(instance, component);
189
+ var scrollableParents: Element[] = (component.scrollableParents = [
190
+ window as any,
191
+ ]);
192
+ component.updateDropdownPosition = (e: any) =>
201
193
  this.updateDropdownPosition(instance, component);
202
- }
203
-
204
- overlayWillUnmount(instance: InstanceType, component: any): void {
205
- var { scrollableParents } = component;
206
- if (scrollableParents) {
207
- scrollableParents.forEach((el: Element) => {
208
- el.removeEventListener("scroll", component.updateDropdownPosition);
209
- });
210
- delete component.scrollableParents;
211
- delete component.updateDropdownPosition;
212
- }
213
- if (component.offResize) component.offResize();
214
-
215
- if (this.pipeValidateDropdownPosition) instance.invoke("pipeValidateDropdownPosition", null, instance);
216
-
217
- if (component.offParentPositionChange) component.offParentPositionChange();
218
-
219
- delete component.parentBounds;
220
- delete component.initialScreenPosition;
221
- }
222
-
223
- dismissAfterScroll(data: any, instance: InstanceType, component: any): void {
224
- if (this.onDismissAfterScroll && instance.invoke("onDismissAfterScroll", data, instance, component) === false)
225
- return;
226
- if (instance.dismiss) instance.dismiss();
227
- }
228
-
229
- updateDropdownPosition(instance: InstanceType, component: any): void {
230
- var { el, initialScreenPosition } = component;
231
- var { data, relatedElement } = instance;
232
- var parentBounds = getTopLevelBoundingClientRect(relatedElement!);
233
-
234
- //getBoundingClientRect() will return an empty rect if the element is hidden or removed
235
- if (parentBounds.left == 0 && parentBounds.top == 0 && parentBounds.bottom == 0 && parentBounds.right == 0) {
236
- if (!component.parentBounds) return;
237
- parentBounds = component.parentBounds;
238
- } else component.parentBounds = parentBounds;
239
-
240
- if (this.trackMouseX && instance.mousePosition) {
241
- parentBounds = new DOMRect(
242
- instance.mousePosition.x,
243
- parentBounds.top,
244
- 0,
245
- parentBounds.bottom - parentBounds.top,
246
- );
247
- }
248
194
 
249
- if (this.trackMouseY && instance.mousePosition) {
250
- parentBounds = new DOMRect(
251
- parentBounds.left,
252
- instance.mousePosition.y,
253
- parentBounds.right - parentBounds.left,
254
- 0,
255
- );
256
- }
257
-
258
- let explode = this.pad && typeof this.elementExplode === "number" ? this.elementExplode : 0;
259
- if (explode) {
260
- parentBounds = new DOMRect(
261
- Math.round(parentBounds.left - explode),
262
- Math.round(parentBounds.top - explode),
263
- Math.round(parentBounds.right - parentBounds.left + 2 * explode),
264
- Math.round(parentBounds.bottom - parentBounds.top + 2 * explode),
265
- );
266
- }
267
-
268
- var style: any = {};
269
- if (this.matchWidth) style.minWidth = `${parentBounds.right - parentBounds.left}px`;
270
- if (this.matchMaxWidth) style.maxWidth = `${parentBounds.right - parentBounds.left}px`;
271
-
272
- var contentSize = this.measureNaturalDropdownSize(instance, component);
273
- var placement = this.findOptimalPlacement(contentSize, parentBounds, data.placement, component.lastPlacement);
274
-
275
- this.applyPositioningPlacementStyles(style, placement, contentSize, parentBounds, el, false);
276
- component.setCustomStyle(style);
277
- this.setDirectionClass(component, placement);
278
-
279
- if (this.constrain) {
280
- //recheck content size for changes as sometimes when auto is used the size can change
281
- let newContentSize = this.measureNaturalDropdownSize(instance, component);
282
- if (newContentSize.width != contentSize.width || newContentSize.height != contentSize.height) {
283
- let newStyle = {};
284
- this.applyPositioningPlacementStyles(newStyle, placement, newContentSize, parentBounds, el, true);
285
- component.setCustomStyle(newStyle);
286
- }
287
- }
288
-
289
- if (!initialScreenPosition) initialScreenPosition = component.initialScreenPosition = parentBounds;
290
-
291
- if (
292
- (!this.trackMouseY && Math.abs(parentBounds.top - initialScreenPosition.top) > this.closeOnScrollDistance) ||
293
- (!this.trackMouseX && Math.abs(parentBounds.left - initialScreenPosition.left) > this.closeOnScrollDistance)
294
- )
295
- this.dismissAfterScroll({ parentBounds, initialScreenPosition }, instance, component);
296
-
297
- instance.positionChangeSubscribers.notify();
298
- }
299
-
300
- applyFixedPositioningPlacementStyles(
301
- style: any,
302
- placement: string,
303
- contentSize: any,
304
- rel: any,
305
- el: HTMLElement,
306
- noAuto: boolean,
307
- ): void {
308
- let viewport = getViewportRect(this.screenPadding);
309
- style.position = "fixed";
310
-
311
- if (placement.startsWith("down")) {
312
- style.top = `${(this.cover ? rel.top : rel.bottom) + this.offset}px`;
313
- let bottom = viewport.bottom - (rel.bottom + this.offset + contentSize.height);
314
- style.bottom =
315
- this.constrain && (noAuto || bottom < this.screenPadding + 10)
316
- ? Math.max(this.screenPadding, bottom) + "px"
317
- : "auto";
318
- } else if (placement.startsWith("up")) {
319
- let top = rel.top - this.offset - contentSize.height - viewport.top;
320
- style.top =
321
- this.constrain && (noAuto || top < this.screenPadding + 10)
322
- ? Math.max(this.screenPadding, top) + "px"
323
- : "auto";
324
- style.bottom =
325
- document.documentElement.offsetHeight - (this.cover ? rel.bottom : rel.top) + this.offset + "px";
326
- }
327
-
328
- switch (placement) {
329
- case "down":
330
- case "down-center":
331
- style.right = "auto";
332
- style.left = `${Math.round((rel.left + rel.right - el.offsetWidth) / 2)}px`;
333
- break;
334
-
335
- case "down-right":
336
- style.right = "auto";
337
- style.left = `${rel.left}px`;
338
- break;
339
-
340
- case "down-left":
341
- style.right = `${document.documentElement.offsetWidth - rel.right}px`;
342
- style.left = "auto";
343
- break;
344
-
345
- case "up":
346
- case "up-center":
347
- style.right = "auto";
348
- style.left = `${Math.round((rel.left + rel.right - el.offsetWidth) / 2)}px`;
349
- break;
350
-
351
- case "up-right":
352
- style.right = "auto";
353
- style.left = `${rel.left}px`;
354
- break;
355
-
356
- case "up-left":
357
- style.right = `${document.documentElement.offsetWidth - rel.right}px`;
358
- style.left = "auto";
359
- break;
360
-
361
- case "right":
362
- case "right-center":
363
- style.top = `${Math.round((rel.top + rel.bottom - el.offsetHeight) / 2)}px`;
364
- style.right = "auto";
365
- style.bottom = "auto";
366
- style.left = `${rel.right + this.offset}px`;
367
- break;
368
-
369
- case "right-down":
370
- style.top = `${rel.top}px`;
371
- style.right = "auto";
372
- style.bottom = "auto";
373
- style.left = `${rel.right + this.offset}px`;
374
- break;
375
-
376
- case "right-up":
377
- style.top = "auto";
378
- style.right = "auto";
379
- style.bottom = `${document.documentElement.offsetHeight - rel.bottom}px`;
380
- style.left = `${rel.right + this.offset}px`;
381
- break;
382
-
383
- case "left":
384
- case "left-center":
385
- style.top = `${Math.round((rel.top + rel.bottom - el.offsetHeight) / 2)}px`;
386
- style.right = `${document.documentElement.offsetWidth - rel.left + this.offset}px`;
387
- style.bottom = "auto";
388
- style.left = "auto";
389
- break;
390
-
391
- case "left-down":
392
- style.top = `${rel.top}px`;
393
- style.right = `${document.documentElement.offsetWidth - rel.left + this.offset}px`;
394
- style.bottom = "auto";
395
- style.left = "auto";
396
- break;
397
-
398
- case "left-up":
399
- style.top = "auto";
400
- style.right = `${document.documentElement.offsetWidth - rel.left + this.offset}px`;
401
- style.bottom = `${document.documentElement.offsetHeight - rel.bottom}px`;
402
- style.left = "auto";
403
- break;
404
-
405
- case "screen-center":
406
- let w = Math.min(contentSize.width, document.documentElement.offsetWidth - 2 * this.screenPadding);
407
- let h = Math.min(contentSize.height, document.documentElement.offsetHeight - 2 * this.screenPadding);
408
- style.top = `${Math.round((document.documentElement.offsetHeight - h) / 2)}px`;
409
- style.right = `${Math.round((document.documentElement.offsetWidth - w) / 2)}px`;
410
- style.bottom = `${Math.round((document.documentElement.offsetHeight - h) / 2)}px`;
411
- style.left = `${Math.round((document.documentElement.offsetWidth - w) / 2)}px`;
412
- break;
413
- }
414
- }
415
-
416
- applyAbsolutePositioningPlacementStyles(
417
- style: any,
418
- placement: string,
419
- contentSize: any,
420
- rel: any,
421
- el: HTMLElement,
422
- noAuto: boolean,
423
- ): void {
424
- var viewport = getViewportRect(this.screenPadding);
425
-
426
- style.position = "absolute";
427
-
428
- if (placement.startsWith("down")) {
429
- style.top = `${rel.bottom - rel.top + this.offset}px`;
430
- let room = viewport.bottom - rel.bottom + this.offset;
431
- style.bottom =
432
- this.constrain && (noAuto || contentSize.height >= room - 10)
433
- ? `${-Math.min(room, contentSize.height)}px`
434
- : "auto";
435
- } else if (placement.startsWith("up")) {
436
- let room = rel.top - this.offset - viewport.top;
437
- style.top =
438
- this.constrain && (noAuto || contentSize.height >= room - 10)
439
- ? `${-Math.min(room, contentSize.height)}px`
440
- : "auto";
441
- style.bottom = `${rel.bottom - rel.top - this.offset}px`;
442
- }
443
-
444
- switch (placement) {
445
- case "down":
446
- case "down-center":
447
- style.right = "auto";
448
- style.left = `${Math.round((rel.right - rel.left - el.offsetWidth) / 2)}px`;
449
- break;
450
-
451
- case "down-right":
452
- style.right = "auto";
453
- style.left = `0`;
454
- break;
455
-
456
- case "down-left":
457
- style.right = `0`;
458
- style.left = "auto";
459
- break;
460
-
461
- case "up":
462
- case "up-center":
463
- style.right = "auto";
464
- style.left = `${Math.round((rel.right - rel.left - el.offsetWidth) / 2)}px`;
465
- break;
466
-
467
- case "up-right":
468
- style.right = "auto";
469
- style.left = `0`;
470
- break;
471
-
472
- case "up-left":
473
- style.right = `0`;
474
- style.left = "auto";
475
- break;
476
-
477
- case "right":
478
- case "right-center":
479
- style.top = `${Math.round((rel.bottom - rel.top - el.offsetHeight) / 2)}px`;
480
- style.right = "auto";
481
- style.bottom = "auto";
482
- style.left = `${rel.right - rel.left + this.offset}px`;
483
- break;
484
-
485
- case "right-down":
486
- style.top = `0`;
487
- style.right = "auto";
488
- style.bottom = "auto";
489
- style.left = `${rel.right - rel.left + this.offset}px`;
490
- break;
491
-
492
- case "right-up":
493
- style.top = "auto";
494
- style.right = "auto";
495
- style.bottom = `0`;
496
- style.left = `${rel.right - rel.left + this.offset}px`;
497
- break;
498
-
499
- case "left":
500
- case "left-center":
501
- style.top = `${Math.round((rel.bottom - rel.top - el.offsetHeight) / 2)}px`;
502
- style.right = `${rel.right - rel.left + this.offset}px`;
503
- style.bottom = "auto";
504
- style.left = "auto";
505
- break;
506
-
507
- case "left-down":
508
- style.top = `0`;
509
- style.right = `${rel.right - rel.left + this.offset}px`;
510
- style.bottom = "auto";
511
- style.left = "auto";
512
- break;
513
-
514
- case "left-up":
515
- style.top = "auto";
516
- style.right = `${rel.right - rel.left + this.offset}px`;
517
- style.bottom = `0`;
518
- style.left = "auto";
519
- break;
520
- }
521
- }
522
-
523
- applyPositioningPlacementStyles(
524
- style: any,
525
- placement: string,
526
- contentSize: any,
527
- parentBounds: any,
528
- el: HTMLElement,
529
- noAuto: boolean,
530
- ): void {
531
- switch (this.positioning) {
532
- case "absolute":
533
- this.applyAbsolutePositioningPlacementStyles(style, placement, contentSize, parentBounds, el, noAuto);
534
- break;
535
-
536
- case "auto":
537
- if (isTouchDevice())
538
- this.applyAbsolutePositioningPlacementStyles(style, placement, contentSize, parentBounds, el, noAuto);
539
- else this.applyFixedPositioningPlacementStyles(style, placement, contentSize, parentBounds, el, noAuto);
540
- break;
541
-
542
- default:
543
- this.applyFixedPositioningPlacementStyles(style, placement, contentSize, parentBounds, el, noAuto);
544
- break;
545
- }
546
- }
547
-
548
- setDirectionClass(component: any, placement: string): void {
549
- var state = {
550
- "place-left": false,
551
- "place-right": false,
552
- "place-up": false,
553
- "place-down": false,
554
- };
555
-
556
- component.lastPlacement = placement;
557
-
558
- component.setCSSState({
559
- ...state,
560
- ["place-" + placement]: true,
195
+ instance.initialScreenPosition = null;
196
+
197
+ var el = instance.relatedElement?.parentElement;
198
+ while (el) {
199
+ scrollableParents.push(el);
200
+ el = el.parentElement;
201
+ }
202
+ scrollableParents.forEach((el: any) => {
203
+ el.addEventListener("scroll", component.updateDropdownPosition);
204
+ });
205
+ component.offResize = ResizeManager.subscribe(
206
+ component.updateDropdownPosition,
207
+ );
208
+
209
+ if (this.onDropdownDidMount)
210
+ instance.invoke("onDropdownDidMount", instance, component);
211
+
212
+ if (this.pipeValidateDropdownPosition)
213
+ instance.invoke(
214
+ "pipeValidateDropdownPosition",
215
+ component.updateDropdownPosition,
216
+ instance,
217
+ );
218
+
219
+ if (instance.parentPositionChangeEvent)
220
+ component.offParentPositionChange =
221
+ instance.parentPositionChangeEvent.subscribe(
222
+ component.updateDropdownPosition,
223
+ );
224
+ }
225
+
226
+ overlayDidUpdate(instance: InstanceType, component: any): void {
227
+ this.updateDropdownPosition(instance, component);
228
+ }
229
+
230
+ overlayWillUnmount(instance: InstanceType, component: any): void {
231
+ var { scrollableParents } = component;
232
+ if (scrollableParents) {
233
+ scrollableParents.forEach((el: Element) => {
234
+ el.removeEventListener("scroll", component.updateDropdownPosition);
561
235
  });
562
- }
563
-
564
- measureNaturalDropdownSize(instance: InstanceType, component: any): any {
565
- var { el } = component;
566
- var size = {
567
- width: el.offsetWidth,
568
- height: this.constrain
569
- ? calculateNaturalElementHeight(el)
570
- : el.offsetHeight - el.clientHeight + el.scrollHeight,
571
- };
572
-
573
- if (this.firstChildDefinesHeight && el.firstChild) {
574
- size.height = el.firstChild.offsetHeight;
575
- }
576
-
577
- if (this.firstChildDefinesWidth && el.firstChild) {
578
- size.width = el.firstChild.offsetWidth;
579
- }
580
-
581
- if (this.onMeasureNaturalContentSize) {
582
- var more = instance.invoke("onMeasureNaturalContentSize", el, instance, component);
583
- Object.assign(size, more);
584
- }
585
-
586
- return size;
587
- }
588
-
589
- findOptimalPlacement(contentSize: any, target: any, placement: string, lastPlacement: any): any {
590
- var placementOrder = this.placementOrder.split(" ");
591
- var best = lastPlacement || placement;
592
- var first;
593
-
594
- var score: Record<string, number> = {};
595
- var viewport = getViewportRect();
596
-
597
- for (var i = 0; i < placementOrder.length; i++) {
598
- var p = placementOrder[i];
599
- if (!first) first = p;
600
- var parts = p.split("-");
601
-
602
- var primary = parts[0];
603
- var secondary = parts[1] || "center";
604
-
605
- score[p] = 0;
606
- var vertical = true;
607
-
608
- switch (primary) {
609
- case "down":
610
- score[p] += 3 * Math.min(1, (viewport.bottom - target.bottom - this.offset) / contentSize.height);
611
- break;
612
-
613
- case "up":
614
- score[p] += 3 * Math.min(1, (target.top - viewport.top - this.offset) / contentSize.height);
615
- break;
616
-
617
- case "right":
618
- score[p] += target.right + contentSize.width + this.offset < viewport.right ? 3 : 0;
619
- vertical = false;
620
- break;
621
-
622
- case "left":
623
- score[p] += target.left - contentSize.width - this.offset >= viewport.left ? 3 : 0;
624
- vertical = false;
625
- break;
626
- }
627
-
628
- switch (secondary) {
629
- case "center":
630
- if (vertical) {
631
- score[p] += (target.right + target.left - contentSize.width) / 2 >= viewport.left ? 1 : 0;
632
- score[p] += (target.right + target.left + contentSize.width) / 2 < viewport.right ? 1 : 0;
633
- } else {
634
- score[p] += (target.bottom + target.top - contentSize.height) / 2 >= viewport.top ? 1 : 0;
635
- score[p] += (target.bottom + target.top + contentSize.height) / 2 < viewport.bottom ? 1 : 0;
636
- }
637
- break;
638
-
639
- case "right":
640
- score[p] += target.left + contentSize.width < viewport.right ? 2 : 0;
641
- break;
642
-
643
- case "left":
644
- score[p] += target.right - contentSize.width >= viewport.left ? 2 : 0;
645
- break;
646
-
647
- case "up":
648
- score[p] += target.bottom - contentSize.height >= viewport.top ? 2 : 0;
649
- break;
650
-
651
- case "down":
652
- score[p] += target.top + contentSize.height < viewport.bottom ? 2 : 0;
653
- break;
654
- }
236
+ delete component.scrollableParents;
237
+ delete component.updateDropdownPosition;
238
+ }
239
+ if (component.offResize) component.offResize();
240
+
241
+ if (this.pipeValidateDropdownPosition)
242
+ instance.invoke("pipeValidateDropdownPosition", null, instance);
243
+
244
+ if (component.offParentPositionChange) component.offParentPositionChange();
245
+
246
+ delete component.parentBounds;
247
+ delete component.initialScreenPosition;
248
+ }
249
+
250
+ dismissAfterScroll(data: any, instance: InstanceType, component: any): void {
251
+ if (
252
+ this.onDismissAfterScroll &&
253
+ instance.invoke("onDismissAfterScroll", data, instance, component) ===
254
+ false
255
+ )
256
+ return;
257
+ if (instance.dismiss) instance.dismiss();
258
+ }
259
+
260
+ updateDropdownPosition(instance: InstanceType, component: any): void {
261
+ var { el, initialScreenPosition } = component;
262
+ var { data, relatedElement } = instance;
263
+ var parentBounds = getTopLevelBoundingClientRect(relatedElement!);
264
+
265
+ //getBoundingClientRect() will return an empty rect if the element is hidden or removed
266
+ if (
267
+ parentBounds.left == 0 &&
268
+ parentBounds.top == 0 &&
269
+ parentBounds.bottom == 0 &&
270
+ parentBounds.right == 0
271
+ ) {
272
+ if (!component.parentBounds) return;
273
+ parentBounds = component.parentBounds;
274
+ } else component.parentBounds = parentBounds;
275
+
276
+ if (this.trackMouseX && instance.mousePosition) {
277
+ parentBounds = new DOMRect(
278
+ instance.mousePosition.x,
279
+ parentBounds.top,
280
+ 0,
281
+ parentBounds.bottom - parentBounds.top,
282
+ );
283
+ }
284
+
285
+ if (this.trackMouseY && instance.mousePosition) {
286
+ parentBounds = new DOMRect(
287
+ parentBounds.left,
288
+ instance.mousePosition.y,
289
+ parentBounds.right - parentBounds.left,
290
+ 0,
291
+ );
292
+ }
293
+
294
+ let explode =
295
+ this.pad && typeof this.elementExplode === "number"
296
+ ? this.elementExplode
297
+ : 0;
298
+ if (explode) {
299
+ parentBounds = new DOMRect(
300
+ Math.round(parentBounds.left - explode),
301
+ Math.round(parentBounds.top - explode),
302
+ Math.round(parentBounds.right - parentBounds.left + 2 * explode),
303
+ Math.round(parentBounds.bottom - parentBounds.top + 2 * explode),
304
+ );
305
+ }
306
+
307
+ var style: any = {};
308
+ if (this.matchWidth)
309
+ style.minWidth = `${parentBounds.right - parentBounds.left}px`;
310
+ if (this.matchMaxWidth)
311
+ style.maxWidth = `${parentBounds.right - parentBounds.left}px`;
312
+
313
+ var contentSize = this.measureNaturalDropdownSize(instance, component);
314
+ var placement = this.findOptimalPlacement(
315
+ contentSize,
316
+ parentBounds,
317
+ data.placement,
318
+ component.lastPlacement,
319
+ );
320
+
321
+ this.applyPositioningPlacementStyles(
322
+ style,
323
+ placement,
324
+ contentSize,
325
+ parentBounds,
326
+ el,
327
+ false,
328
+ );
329
+ component.setCustomStyle(style);
330
+ this.setDirectionClass(component, placement);
331
+
332
+ if (this.constrain) {
333
+ //recheck content size for changes as sometimes when auto is used the size can change
334
+ let newContentSize = this.measureNaturalDropdownSize(instance, component);
335
+ if (
336
+ newContentSize.width != contentSize.width ||
337
+ newContentSize.height != contentSize.height
338
+ ) {
339
+ let newStyle = {};
340
+ this.applyPositioningPlacementStyles(
341
+ newStyle,
342
+ placement,
343
+ newContentSize,
344
+ parentBounds,
345
+ el,
346
+ true,
347
+ );
348
+ component.setCustomStyle(newStyle);
655
349
  }
656
-
657
- if (!(best in score)) best = first;
658
-
659
- for (var k in score) if (score[k] > score[best]) best = k;
660
-
661
- if (this.touchFriendly && isTouchDevice() && score[best] < 5) return "screen-center";
662
-
663
- return best;
664
- }
665
-
666
- handleKeyDown(e: React.KeyboardEvent, instance: InstanceType) {
667
- switch (e.keyCode) {
668
- case 27: //esc
669
- var focusable = findFirst(instance.relatedElement!, isFocusable);
670
- if (focusable) focusable.focus();
671
- e.stopPropagation();
672
- e.preventDefault();
673
- break;
350
+ }
351
+
352
+ if (!initialScreenPosition)
353
+ initialScreenPosition = component.initialScreenPosition = parentBounds;
354
+
355
+ if (
356
+ (!this.trackMouseY &&
357
+ Math.abs(parentBounds.top - initialScreenPosition.top) >
358
+ this.closeOnScrollDistance) ||
359
+ (!this.trackMouseX &&
360
+ Math.abs(parentBounds.left - initialScreenPosition.left) >
361
+ this.closeOnScrollDistance)
362
+ )
363
+ this.dismissAfterScroll(
364
+ { parentBounds, initialScreenPosition },
365
+ instance,
366
+ component,
367
+ );
368
+
369
+ instance.positionChangeSubscribers.notify();
370
+ }
371
+
372
+ applyFixedPositioningPlacementStyles(
373
+ style: any,
374
+ placement: string,
375
+ contentSize: any,
376
+ rel: any,
377
+ el: HTMLElement,
378
+ noAuto: boolean,
379
+ ): void {
380
+ let viewport = getViewportRect(this.screenPadding);
381
+ style.position = "fixed";
382
+
383
+ if (placement.startsWith("down")) {
384
+ style.top = `${(this.cover ? rel.top : rel.bottom) + this.offset}px`;
385
+ let bottom =
386
+ viewport.bottom - (rel.bottom + this.offset + contentSize.height);
387
+ style.bottom =
388
+ this.constrain && (noAuto || bottom < this.screenPadding + 10)
389
+ ? Math.max(this.screenPadding, bottom) + "px"
390
+ : "auto";
391
+ } else if (placement.startsWith("up")) {
392
+ let top = rel.top - this.offset - contentSize.height - viewport.top;
393
+ style.top =
394
+ this.constrain && (noAuto || top < this.screenPadding + 10)
395
+ ? Math.max(this.screenPadding, top) + "px"
396
+ : "auto";
397
+ style.bottom =
398
+ document.documentElement.offsetHeight -
399
+ (this.cover ? rel.bottom : rel.top) +
400
+ this.offset +
401
+ "px";
402
+ }
403
+
404
+ switch (placement) {
405
+ case "down":
406
+ case "down-center":
407
+ style.right = "auto";
408
+ style.left = `${Math.round((rel.left + rel.right - el.offsetWidth) / 2)}px`;
409
+ break;
410
+
411
+ case "down-right":
412
+ style.right = "auto";
413
+ style.left = `${rel.left}px`;
414
+ break;
415
+
416
+ case "down-left":
417
+ style.right = `${document.documentElement.offsetWidth - rel.right}px`;
418
+ style.left = "auto";
419
+ break;
420
+
421
+ case "up":
422
+ case "up-center":
423
+ style.right = "auto";
424
+ style.left = `${Math.round((rel.left + rel.right - el.offsetWidth) / 2)}px`;
425
+ break;
426
+
427
+ case "up-right":
428
+ style.right = "auto";
429
+ style.left = `${rel.left}px`;
430
+ break;
431
+
432
+ case "up-left":
433
+ style.right = `${document.documentElement.offsetWidth - rel.right}px`;
434
+ style.left = "auto";
435
+ break;
436
+
437
+ case "right":
438
+ case "right-center":
439
+ style.top = `${Math.round((rel.top + rel.bottom - el.offsetHeight) / 2)}px`;
440
+ style.right = "auto";
441
+ style.bottom = "auto";
442
+ style.left = `${rel.right + this.offset}px`;
443
+ break;
444
+
445
+ case "right-down":
446
+ style.top = `${rel.top}px`;
447
+ style.right = "auto";
448
+ style.bottom = "auto";
449
+ style.left = `${rel.right + this.offset}px`;
450
+ break;
451
+
452
+ case "right-up":
453
+ style.top = "auto";
454
+ style.right = "auto";
455
+ style.bottom = `${document.documentElement.offsetHeight - rel.bottom}px`;
456
+ style.left = `${rel.right + this.offset}px`;
457
+ break;
458
+
459
+ case "left":
460
+ case "left-center":
461
+ style.top = `${Math.round((rel.top + rel.bottom - el.offsetHeight) / 2)}px`;
462
+ style.right = `${document.documentElement.offsetWidth - rel.left + this.offset}px`;
463
+ style.bottom = "auto";
464
+ style.left = "auto";
465
+ break;
466
+
467
+ case "left-down":
468
+ style.top = `${rel.top}px`;
469
+ style.right = `${document.documentElement.offsetWidth - rel.left + this.offset}px`;
470
+ style.bottom = "auto";
471
+ style.left = "auto";
472
+ break;
473
+
474
+ case "left-up":
475
+ style.top = "auto";
476
+ style.right = `${document.documentElement.offsetWidth - rel.left + this.offset}px`;
477
+ style.bottom = `${document.documentElement.offsetHeight - rel.bottom}px`;
478
+ style.left = "auto";
479
+ break;
480
+
481
+ case "screen-center":
482
+ let w = Math.min(
483
+ contentSize.width,
484
+ document.documentElement.offsetWidth - 2 * this.screenPadding,
485
+ );
486
+ let h = Math.min(
487
+ contentSize.height,
488
+ document.documentElement.offsetHeight - 2 * this.screenPadding,
489
+ );
490
+ style.top = `${Math.round((document.documentElement.offsetHeight - h) / 2)}px`;
491
+ style.right = `${Math.round((document.documentElement.offsetWidth - w) / 2)}px`;
492
+ style.bottom = `${Math.round((document.documentElement.offsetHeight - h) / 2)}px`;
493
+ style.left = `${Math.round((document.documentElement.offsetWidth - w) / 2)}px`;
494
+ break;
495
+ }
496
+ }
497
+
498
+ applyAbsolutePositioningPlacementStyles(
499
+ style: any,
500
+ placement: string,
501
+ contentSize: any,
502
+ rel: any,
503
+ el: HTMLElement,
504
+ noAuto: boolean,
505
+ ): void {
506
+ var viewport = getViewportRect(this.screenPadding);
507
+
508
+ style.position = "absolute";
509
+
510
+ if (placement.startsWith("down")) {
511
+ style.top = `${rel.bottom - rel.top + this.offset}px`;
512
+ let room = viewport.bottom - rel.bottom + this.offset;
513
+ style.bottom =
514
+ this.constrain && (noAuto || contentSize.height >= room - 10)
515
+ ? `${-Math.min(room, contentSize.height)}px`
516
+ : "auto";
517
+ } else if (placement.startsWith("up")) {
518
+ let room = rel.top - this.offset - viewport.top;
519
+ style.top =
520
+ this.constrain && (noAuto || contentSize.height >= room - 10)
521
+ ? `${-Math.min(room, contentSize.height)}px`
522
+ : "auto";
523
+ style.bottom = `${rel.bottom - rel.top - this.offset}px`;
524
+ }
525
+
526
+ switch (placement) {
527
+ case "down":
528
+ case "down-center":
529
+ style.right = "auto";
530
+ style.left = `${Math.round((rel.right - rel.left - el.offsetWidth) / 2)}px`;
531
+ break;
532
+
533
+ case "down-right":
534
+ style.right = "auto";
535
+ style.left = `0`;
536
+ break;
537
+
538
+ case "down-left":
539
+ style.right = `0`;
540
+ style.left = "auto";
541
+ break;
542
+
543
+ case "up":
544
+ case "up-center":
545
+ style.right = "auto";
546
+ style.left = `${Math.round((rel.right - rel.left - el.offsetWidth) / 2)}px`;
547
+ break;
548
+
549
+ case "up-right":
550
+ style.right = "auto";
551
+ style.left = `0`;
552
+ break;
553
+
554
+ case "up-left":
555
+ style.right = `0`;
556
+ style.left = "auto";
557
+ break;
558
+
559
+ case "right":
560
+ case "right-center":
561
+ style.top = `${Math.round((rel.bottom - rel.top - el.offsetHeight) / 2)}px`;
562
+ style.right = "auto";
563
+ style.bottom = "auto";
564
+ style.left = `${rel.right - rel.left + this.offset}px`;
565
+ break;
566
+
567
+ case "right-down":
568
+ style.top = `0`;
569
+ style.right = "auto";
570
+ style.bottom = "auto";
571
+ style.left = `${rel.right - rel.left + this.offset}px`;
572
+ break;
573
+
574
+ case "right-up":
575
+ style.top = "auto";
576
+ style.right = "auto";
577
+ style.bottom = `0`;
578
+ style.left = `${rel.right - rel.left + this.offset}px`;
579
+ break;
580
+
581
+ case "left":
582
+ case "left-center":
583
+ style.top = `${Math.round((rel.bottom - rel.top - el.offsetHeight) / 2)}px`;
584
+ style.right = `${rel.right - rel.left + this.offset}px`;
585
+ style.bottom = "auto";
586
+ style.left = "auto";
587
+ break;
588
+
589
+ case "left-down":
590
+ style.top = `0`;
591
+ style.right = `${rel.right - rel.left + this.offset}px`;
592
+ style.bottom = "auto";
593
+ style.left = "auto";
594
+ break;
595
+
596
+ case "left-up":
597
+ style.top = "auto";
598
+ style.right = `${rel.right - rel.left + this.offset}px`;
599
+ style.bottom = `0`;
600
+ style.left = "auto";
601
+ break;
602
+ }
603
+ }
604
+
605
+ applyPositioningPlacementStyles(
606
+ style: any,
607
+ placement: string,
608
+ contentSize: any,
609
+ parentBounds: any,
610
+ el: HTMLElement,
611
+ noAuto: boolean,
612
+ ): void {
613
+ switch (this.positioning) {
614
+ case "absolute":
615
+ this.applyAbsolutePositioningPlacementStyles(
616
+ style,
617
+ placement,
618
+ contentSize,
619
+ parentBounds,
620
+ el,
621
+ noAuto,
622
+ );
623
+ break;
624
+
625
+ case "auto":
626
+ if (isTouchDevice())
627
+ this.applyAbsolutePositioningPlacementStyles(
628
+ style,
629
+ placement,
630
+ contentSize,
631
+ parentBounds,
632
+ el,
633
+ noAuto,
634
+ );
635
+ else
636
+ this.applyFixedPositioningPlacementStyles(
637
+ style,
638
+ placement,
639
+ contentSize,
640
+ parentBounds,
641
+ el,
642
+ noAuto,
643
+ );
644
+ break;
645
+
646
+ default:
647
+ this.applyFixedPositioningPlacementStyles(
648
+ style,
649
+ placement,
650
+ contentSize,
651
+ parentBounds,
652
+ el,
653
+ noAuto,
654
+ );
655
+ break;
656
+ }
657
+ }
658
+
659
+ setDirectionClass(component: any, placement: string): void {
660
+ var state = {
661
+ "place-left": false,
662
+ "place-right": false,
663
+ "place-up": false,
664
+ "place-down": false,
665
+ };
666
+
667
+ component.lastPlacement = placement;
668
+
669
+ component.setCSSState({
670
+ ...state,
671
+ ["place-" + placement]: true,
672
+ });
673
+ }
674
+
675
+ measureNaturalDropdownSize(instance: InstanceType, component: any): any {
676
+ var { el } = component;
677
+ var size = {
678
+ width: el.offsetWidth,
679
+ height: this.constrain
680
+ ? calculateNaturalElementHeight(el)
681
+ : el.offsetHeight - el.clientHeight + el.scrollHeight,
682
+ };
683
+
684
+ if (this.firstChildDefinesHeight && el.firstChild) {
685
+ size.height = el.firstChild.offsetHeight;
686
+ }
687
+
688
+ if (this.firstChildDefinesWidth && el.firstChild) {
689
+ size.width = el.firstChild.offsetWidth;
690
+ }
691
+
692
+ if (this.onMeasureNaturalContentSize) {
693
+ var more = instance.invoke(
694
+ "onMeasureNaturalContentSize",
695
+ el,
696
+ instance,
697
+ component,
698
+ );
699
+ Object.assign(size, more);
700
+ }
701
+
702
+ return size;
703
+ }
704
+
705
+ findOptimalPlacement(
706
+ contentSize: any,
707
+ target: any,
708
+ placement: string,
709
+ lastPlacement: any,
710
+ ): any {
711
+ var placementOrder = this.placementOrder.split(" ");
712
+ var best = lastPlacement || placement;
713
+ var first;
714
+
715
+ var score: Record<string, number> = {};
716
+ var viewport = getViewportRect();
717
+
718
+ for (var i = 0; i < placementOrder.length; i++) {
719
+ var p = placementOrder[i];
720
+ if (!first) first = p;
721
+ var parts = p.split("-");
722
+
723
+ var primary = parts[0];
724
+ var secondary = parts[1] || "center";
725
+
726
+ score[p] = 0;
727
+ var vertical = true;
728
+
729
+ switch (primary) {
730
+ case "down":
731
+ score[p] +=
732
+ 3 *
733
+ Math.min(
734
+ 1,
735
+ (viewport.bottom - target.bottom - this.offset) /
736
+ contentSize.height,
737
+ );
738
+ break;
739
+
740
+ case "up":
741
+ score[p] +=
742
+ 3 *
743
+ Math.min(
744
+ 1,
745
+ (target.top - viewport.top - this.offset) / contentSize.height,
746
+ );
747
+ break;
748
+
749
+ case "right":
750
+ score[p] +=
751
+ target.right + contentSize.width + this.offset < viewport.right
752
+ ? 3
753
+ : 0;
754
+ vertical = false;
755
+ break;
756
+
757
+ case "left":
758
+ score[p] +=
759
+ target.left - contentSize.width - this.offset >= viewport.left
760
+ ? 3
761
+ : 0;
762
+ vertical = false;
763
+ break;
674
764
  }
675
765
 
676
- if (this.onKeyDown) instance.invoke("onKeyDown", e, instance);
677
- }
678
-
679
- renderContents(context: RenderingContext, instance: InstanceType) {
680
- let { CSS, baseClass } = this;
681
- let result = [super.renderContents(context, instance)];
682
- if (this.arrow) {
683
- result.push(
684
- <div key="arrow-border" className={CSS.element(baseClass, "arrow-border")}></div>,
685
- <div key="arrow-back" className={CSS.element(baseClass, "arrow-fill")}></div>,
686
- );
687
- }
688
- return result;
689
- }
690
-
691
- render(context: RenderingContext, instance: InstanceType, key: string) {
692
- let { CSS, baseClass } = this;
693
- //if relatedElement is not provided, a beacon is rendered to and used to resolve a nearby element as a target
694
- //if onResolveTarget doesn't provide another element, the beacon itself is used as a target
695
- let beacon = null;
696
- if (this.relatedElement) instance.relatedElement = this.relatedElement;
697
-
698
- if (!this.relatedElement || instance.needsBeacon) {
699
- beacon = (
700
- <div
701
- key={`${key}-beacon`}
702
- className={CSS.element(baseClass, "beacon")}
703
- ref={(el) => {
704
- if (instance.relatedElement || !el) return;
705
- let target: HTMLElement | null = el;
706
- if (this.onResolveRelatedElement) target = instance.invoke("onResolveRelatedElement", el, instance);
707
- else target = el.previousElementSibling as HTMLElement;
708
- if (!target) target = el;
709
- if (target == el) instance.needsBeacon = true;
710
- instance.relatedElement = target;
711
- instance.setState({ dummy: {} });
712
- }}
713
- />
714
- );
715
- }
716
- return [beacon, instance.relatedElement && super.render(context, instance, key)];
717
- }
718
-
719
- getOverlayContainer(): HTMLElement {
720
- // this should be instance.relatedElement
721
- if (this.relatedElement) {
722
- let container = closestParent(
723
- this.relatedElement,
724
- (el) => el.dataset && !!el.dataset.focusableOverlayContainer,
725
- );
726
- if (container) return container;
766
+ switch (secondary) {
767
+ case "center":
768
+ if (vertical) {
769
+ score[p] +=
770
+ (target.right + target.left - contentSize.width) / 2 >=
771
+ viewport.left
772
+ ? 1
773
+ : 0;
774
+ score[p] +=
775
+ (target.right + target.left + contentSize.width) / 2 <
776
+ viewport.right
777
+ ? 1
778
+ : 0;
779
+ } else {
780
+ score[p] +=
781
+ (target.bottom + target.top - contentSize.height) / 2 >=
782
+ viewport.top
783
+ ? 1
784
+ : 0;
785
+ score[p] +=
786
+ (target.bottom + target.top + contentSize.height) / 2 <
787
+ viewport.bottom
788
+ ? 1
789
+ : 0;
790
+ }
791
+ break;
792
+
793
+ case "right":
794
+ score[p] += target.left + contentSize.width < viewport.right ? 2 : 0;
795
+ break;
796
+
797
+ case "left":
798
+ score[p] += target.right - contentSize.width >= viewport.left ? 2 : 0;
799
+ break;
800
+
801
+ case "up":
802
+ score[p] +=
803
+ target.bottom - contentSize.height >= viewport.top ? 2 : 0;
804
+ break;
805
+
806
+ case "down":
807
+ score[p] += target.top + contentSize.height < viewport.bottom ? 2 : 0;
808
+ break;
727
809
  }
728
- return super.getOverlayContainer();
729
- }
810
+ }
811
+
812
+ if (!(best in score)) best = first;
813
+
814
+ for (var k in score) if (score[k] > score[best]) best = k;
815
+
816
+ if (this.touchFriendly && isTouchDevice() && score[best] < 5)
817
+ return "screen-center";
818
+
819
+ return best;
820
+ }
821
+
822
+ handleKeyDown(e: React.KeyboardEvent, instance: InstanceType) {
823
+ switch (e.keyCode) {
824
+ case 27: //esc
825
+ var focusable = findFirst(instance.relatedElement!, isFocusable);
826
+ if (focusable) focusable.focus();
827
+ e.stopPropagation();
828
+ e.preventDefault();
829
+ break;
830
+ }
831
+
832
+ if (this.onKeyDown) instance.invoke("onKeyDown", e, instance);
833
+ }
834
+
835
+ renderContents(context: RenderingContext, instance: InstanceType) {
836
+ let { CSS, baseClass } = this;
837
+ let result = [super.renderContents(context, instance)];
838
+ if (this.arrow) {
839
+ result.push(
840
+ <div
841
+ key="arrow-border"
842
+ className={CSS.element(baseClass, "arrow-border")}
843
+ ></div>,
844
+ <div
845
+ key="arrow-back"
846
+ className={CSS.element(baseClass, "arrow-fill")}
847
+ ></div>,
848
+ );
849
+ }
850
+ return result;
851
+ }
852
+
853
+ render(context: RenderingContext, instance: InstanceType, key: string) {
854
+ let { CSS, baseClass } = this;
855
+ //if relatedElement is not provided, a beacon is rendered to and used to resolve a nearby element as a target
856
+ //if onResolveTarget doesn't provide another element, the beacon itself is used as a target
857
+ let beacon = null;
858
+ if (this.relatedElement) instance.relatedElement = this.relatedElement;
859
+
860
+ if (!this.relatedElement || instance.needsBeacon) {
861
+ beacon = (
862
+ <div
863
+ key={`${key}-beacon`}
864
+ className={CSS.element(baseClass, "beacon")}
865
+ ref={(el) => {
866
+ if (instance.relatedElement || !el) return;
867
+ let target: HTMLElement | null = el;
868
+ if (this.onResolveRelatedElement)
869
+ target = instance.invoke("onResolveRelatedElement", el, instance);
870
+ else target = el.previousElementSibling as HTMLElement;
871
+ if (!target) target = el;
872
+ if (target == el) instance.needsBeacon = true;
873
+ instance.relatedElement = target;
874
+ instance.setState({ dummy: {} });
875
+ }}
876
+ />
877
+ );
878
+ }
879
+ return [
880
+ beacon,
881
+ instance.relatedElement && super.render(context, instance, key),
882
+ ];
883
+ }
884
+
885
+ getOverlayContainer(): HTMLElement {
886
+ // this should be instance.relatedElement
887
+ if (this.relatedElement) {
888
+ let container = closestParent(
889
+ this.relatedElement,
890
+ (el) => el.dataset && !!el.dataset.focusableOverlayContainer,
891
+ );
892
+ if (container) return container;
893
+ }
894
+ return super.getOverlayContainer();
895
+ }
896
+
897
+ getConfigureOverlayContainerContext(
898
+ instance?: DropdownInstance,
899
+ ): ConfigureOverlayContainerContext {
900
+ return {
901
+ ...super.getConfigureOverlayContainerContext(),
902
+ relatedElement: instance?.relatedElement ?? this.relatedElement,
903
+ };
904
+ }
730
905
  }
731
906
 
732
907
  DropdownBase.prototype.offset = 0;
@@ -753,10 +928,10 @@ Widget.alias("dropdown", Dropdown);
753
928
  Localization.registerPrototype("cx/widgets/Dropdown", Dropdown);
754
929
 
755
930
  function getViewportRect(padding = 0) {
756
- return {
757
- left: padding,
758
- top: padding,
759
- right: document.documentElement.offsetWidth - padding,
760
- bottom: document.documentElement.offsetHeight - padding,
761
- };
931
+ return {
932
+ left: padding,
933
+ top: padding,
934
+ right: document.documentElement.offsetWidth - padding,
935
+ bottom: document.documentElement.offsetHeight - padding,
936
+ };
762
937
  }