Dashboard sipadu mbip
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

Popper.js.flow 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. // @flow
  2. import * as React from 'react';
  3. import PopperJS, {
  4. type Placement,
  5. type Instance,
  6. type Data,
  7. type Modifiers,
  8. type ReferenceObject,
  9. } from 'popper.js';
  10. import type { Style } from 'typed-styles';
  11. import { ManagerReferenceNodeContext } from './Manager';
  12. import { unwrapArray, setRef, shallowEqual } from './utils';
  13. import { type Ref } from "./RefTypes";
  14. type ReferenceElement = ReferenceObject | HTMLElement | null;
  15. type StyleOffsets = { top: number, left: number };
  16. type StylePosition = { position: 'absolute' | 'fixed' };
  17. export type PopperArrowProps = {
  18. ref: Ref,
  19. style: StyleOffsets & Style,
  20. };
  21. export type PopperChildrenProps = {|
  22. ref: Ref,
  23. style: StyleOffsets & StylePosition & Style,
  24. placement: Placement,
  25. outOfBoundaries: ?boolean,
  26. scheduleUpdate: () => void,
  27. arrowProps: PopperArrowProps,
  28. |};
  29. export type PopperChildren = PopperChildrenProps => React.Node;
  30. export type PopperProps = {
  31. children: PopperChildren,
  32. eventsEnabled?: boolean,
  33. innerRef?: Ref,
  34. modifiers?: Modifiers,
  35. placement?: Placement,
  36. positionFixed?: boolean,
  37. referenceElement?: ReferenceElement,
  38. };
  39. type PopperState = {
  40. data: ?Data,
  41. placement: ?Placement,
  42. };
  43. const initialStyle = {
  44. position: 'absolute',
  45. top: 0,
  46. left: 0,
  47. opacity: 0,
  48. pointerEvents: 'none',
  49. };
  50. const initialArrowStyle = {};
  51. export class InnerPopper extends React.Component<PopperProps, PopperState> {
  52. static defaultProps = {
  53. placement: 'bottom',
  54. eventsEnabled: true,
  55. referenceElement: undefined,
  56. positionFixed: false,
  57. };
  58. state = {
  59. data: undefined,
  60. placement: undefined,
  61. };
  62. popperInstance: ?Instance;
  63. popperNode: ?HTMLElement = null;
  64. arrowNode: ?HTMLElement = null;
  65. setPopperNode = (popperNode: ?HTMLElement) => {
  66. if (!popperNode || this.popperNode === popperNode) return;
  67. setRef(this.props.innerRef, popperNode);
  68. this.popperNode = popperNode;
  69. this.updatePopperInstance();
  70. };
  71. setArrowNode = (arrowNode: ?HTMLElement) => {
  72. this.arrowNode = arrowNode;
  73. };
  74. updateStateModifier = {
  75. enabled: true,
  76. order: 900,
  77. fn: (data: Object) => {
  78. const { placement } = data;
  79. this.setState({ data, placement });
  80. return data;
  81. },
  82. };
  83. getOptions = () => ({
  84. placement: this.props.placement,
  85. eventsEnabled: this.props.eventsEnabled,
  86. positionFixed: this.props.positionFixed,
  87. modifiers: {
  88. ...this.props.modifiers,
  89. arrow: {
  90. ...(this.props.modifiers && this.props.modifiers.arrow),
  91. enabled: !!this.arrowNode,
  92. element: this.arrowNode,
  93. },
  94. applyStyle: { enabled: false },
  95. updateStateModifier: this.updateStateModifier,
  96. },
  97. });
  98. getPopperStyle = () =>
  99. !this.popperNode || !this.state.data
  100. ? initialStyle
  101. : {
  102. position: this.state.data.offsets.popper.position,
  103. ...this.state.data.styles,
  104. };
  105. getPopperPlacement = () =>
  106. !this.state.data ? undefined : this.state.placement;
  107. getArrowStyle = () =>
  108. !this.arrowNode || !this.state.data
  109. ? initialArrowStyle
  110. : this.state.data.arrowStyles;
  111. getOutOfBoundariesState = () =>
  112. this.state.data ? this.state.data.hide : undefined;
  113. destroyPopperInstance = () => {
  114. if (!this.popperInstance) return;
  115. this.popperInstance.destroy();
  116. this.popperInstance = null;
  117. };
  118. updatePopperInstance = () => {
  119. this.destroyPopperInstance();
  120. const { popperNode } = this;
  121. const { referenceElement } = this.props;
  122. if (!referenceElement || !popperNode) return;
  123. this.popperInstance = new PopperJS(
  124. referenceElement,
  125. popperNode,
  126. this.getOptions()
  127. );
  128. };
  129. scheduleUpdate = () => {
  130. if (this.popperInstance) {
  131. this.popperInstance.scheduleUpdate();
  132. }
  133. };
  134. componentDidUpdate(prevProps: PopperProps, prevState: PopperState) {
  135. // If the Popper.js options have changed, update the instance (destroy + create)
  136. if (
  137. this.props.placement !== prevProps.placement ||
  138. this.props.referenceElement !== prevProps.referenceElement ||
  139. this.props.positionFixed !== prevProps.positionFixed ||
  140. this.props.modifiers !== prevProps.modifiers
  141. ) {
  142. // develop only check that modifiers isn't being updated needlessly
  143. if (process.env.NODE_ENV === "development") {
  144. if (
  145. this.props.modifiers !== prevProps.modifiers &&
  146. this.props.modifiers != null &&
  147. prevProps.modifiers != null &&
  148. shallowEqual(this.props.modifiers, prevProps.modifiers)
  149. ) {
  150. console.warn("'modifiers' prop reference updated even though all values appear the same.\nConsider memoizing the 'modifiers' object to avoid needless rendering.");
  151. }
  152. }
  153. this.updatePopperInstance();
  154. } else if (
  155. this.props.eventsEnabled !== prevProps.eventsEnabled &&
  156. this.popperInstance
  157. ) {
  158. this.props.eventsEnabled
  159. ? this.popperInstance.enableEventListeners()
  160. : this.popperInstance.disableEventListeners();
  161. }
  162. // A placement difference in state means popper determined a new placement
  163. // apart from the props value. By the time the popper element is rendered with
  164. // the new position Popper has already measured it, if the place change triggers
  165. // a size change it will result in a misaligned popper. So we schedule an update to be sure.
  166. if (prevState.placement !== this.state.placement) {
  167. this.scheduleUpdate();
  168. }
  169. }
  170. componentWillUnmount() {
  171. setRef(this.props.innerRef, null)
  172. this.destroyPopperInstance();
  173. }
  174. render() {
  175. return unwrapArray(this.props.children)({
  176. ref: this.setPopperNode,
  177. style: this.getPopperStyle(),
  178. placement: this.getPopperPlacement(),
  179. outOfBoundaries: this.getOutOfBoundariesState(),
  180. scheduleUpdate: this.scheduleUpdate,
  181. arrowProps: {
  182. ref: this.setArrowNode,
  183. style: this.getArrowStyle(),
  184. },
  185. });
  186. }
  187. }
  188. const placements = PopperJS.placements;
  189. export { placements };
  190. export default function Popper({ referenceElement, ...props }: PopperProps) {
  191. return (
  192. <ManagerReferenceNodeContext.Consumer>
  193. {(referenceNode) => (
  194. <InnerPopper
  195. referenceElement={
  196. referenceElement !== undefined ? referenceElement : referenceNode
  197. }
  198. {...props}
  199. />
  200. )}
  201. </ManagerReferenceNodeContext.Consumer>
  202. );
  203. }