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