You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
2.7 KiB

2 months ago
  1. // @flow
  2. import type { Modifier, ModifierArguments } from '../types';
  3. import getNodeName from '../dom-utils/getNodeName';
  4. import { isHTMLElement } from '../dom-utils/instanceOf';
  5. // This modifier takes the styles prepared by the `computeStyles` modifier
  6. // and applies them to the HTMLElements such as popper and arrow
  7. function applyStyles({ state }: ModifierArguments<{||}>) {
  8. Object.keys(state.elements).forEach((name) => {
  9. const style = state.styles[name] || {};
  10. const attributes = state.attributes[name] || {};
  11. const element = state.elements[name];
  12. // arrow is optional + virtual elements
  13. if (!isHTMLElement(element) || !getNodeName(element)) {
  14. return;
  15. }
  16. // Flow doesn't support to extend this property, but it's the most
  17. // effective way to apply styles to an HTMLElement
  18. // $FlowFixMe[cannot-write]
  19. Object.assign(element.style, style);
  20. Object.keys(attributes).forEach((name) => {
  21. const value = attributes[name];
  22. if (value === false) {
  23. element.removeAttribute(name);
  24. } else {
  25. element.setAttribute(name, value === true ? '' : value);
  26. }
  27. });
  28. });
  29. }
  30. function effect({ state }: ModifierArguments<{||}>) {
  31. const initialStyles = {
  32. popper: {
  33. position: state.options.strategy,
  34. left: '0',
  35. top: '0',
  36. margin: '0',
  37. },
  38. arrow: {
  39. position: 'absolute',
  40. },
  41. reference: {},
  42. };
  43. Object.assign(state.elements.popper.style, initialStyles.popper);
  44. state.styles = initialStyles;
  45. if (state.elements.arrow) {
  46. Object.assign(state.elements.arrow.style, initialStyles.arrow);
  47. }
  48. return () => {
  49. Object.keys(state.elements).forEach((name) => {
  50. const element = state.elements[name];
  51. const attributes = state.attributes[name] || {};
  52. const styleProperties = Object.keys(
  53. state.styles.hasOwnProperty(name)
  54. ? state.styles[name]
  55. : initialStyles[name]
  56. );
  57. // Set all values to an empty string to unset them
  58. const style = styleProperties.reduce((style, property) => {
  59. style[property] = '';
  60. return style;
  61. }, {});
  62. // arrow is optional + virtual elements
  63. if (!isHTMLElement(element) || !getNodeName(element)) {
  64. return;
  65. }
  66. Object.assign(element.style, style);
  67. Object.keys(attributes).forEach((attribute) => {
  68. element.removeAttribute(attribute);
  69. });
  70. });
  71. };
  72. }
  73. // eslint-disable-next-line import/no-unused-modules
  74. export type ApplyStylesModifier = Modifier<'applyStyles', {||}>;
  75. export default ({
  76. name: 'applyStyles',
  77. enabled: true,
  78. phase: 'write',
  79. fn: applyStyles,
  80. effect,
  81. requires: ['computeStyles'],
  82. }: ApplyStylesModifier);