|
1 |
| -import { Component, ReactNode, createElement } from "react"; |
2 |
| -import { findDOMNode } from "react-dom"; |
3 |
| -import $, { Cash } from "cash-dom"; |
4 |
| -import { hot } from "react-hot-loader/root"; |
| 1 | +import "mutationobserver-polyfill"; |
| 2 | +import { createElement, FC, useRef, useEffect, useCallback } from "react"; |
| 3 | +import $ from "cash-dom"; |
| 4 | + |
| 5 | +import { useMutationObserver, getRefElement } from "./hooks/mutationObserver"; |
5 | 6 | import { AttributeHelperContainerProps } from "../typings/AttributeHelperProps";
|
6 | 7 |
|
7 | 8 | import "./ui/AttributeHelper.css";
|
8 | 9 |
|
9 |
| -const PROHIBITED_ATTRIBUTES = ["class", "style", "widgetid", "data-mendix-id"]; |
10 |
| - |
11 |
| -class AttributeHelper extends Component<AttributeHelperContainerProps> { |
12 |
| - domNode: HTMLElement | null = null; |
13 |
| - |
14 |
| - render(): ReactNode { |
15 |
| - // We'll render an element, as we are using the dom |
16 |
| - return <div className="attributeHelper" />; |
17 |
| - } |
| 10 | +import { doTransformations } from "./transformer"; |
18 | 11 |
|
19 |
| - componentDidUpdate(): void { |
20 |
| - const domNode = findDOMNode(this); |
| 12 | +const AttributeHelper: FC<AttributeHelperContainerProps> = props => { |
| 13 | + const elRef = useRef<HTMLDivElement>(null); |
| 14 | + const bodyRef = getRefElement(document.body); |
21 | 15 |
|
22 |
| - if (domNode instanceof Element) { |
23 |
| - this.domNode = domNode as HTMLElement; |
24 |
| - this.doTransformations(); |
| 16 | + const runTransformer = useCallback(() => { |
| 17 | + if (elRef.current) { |
| 18 | + const domNode = $(elRef.current); |
| 19 | + doTransformations(domNode, props); |
25 | 20 | }
|
26 |
| - } |
| 21 | + }, [elRef.current, props]); |
27 | 22 |
|
28 |
| - private doTransformations(): void { |
29 |
| - const { |
30 |
| - transformations, |
31 |
| - selectorSiblingFilter, |
32 |
| - selectorSelection, |
33 |
| - selectorSiblingSubFilter, |
34 |
| - selectorParentsSelector |
35 |
| - } = this.props; |
36 |
| - if (this.domNode === null) { |
37 |
| - return; |
| 23 | + const handleMutations = useCallback(() => { |
| 24 | + if (props.miscUseMutationObserver) { |
| 25 | + runTransformer(); |
38 | 26 | }
|
39 |
| - const $el = $(this.domNode); |
40 |
| - |
41 |
| - transformations.forEach(transformation => { |
42 |
| - const { |
43 |
| - transformAttribute, |
44 |
| - transformElement, |
45 |
| - transformSiblingFilter, |
46 |
| - transformTextTemplate, |
47 |
| - transformSiblingSubFilter, |
48 |
| - transformParentsSelector, |
49 |
| - transformRemoveSpaces, |
50 |
| - transformTextTransform |
51 |
| - } = transformation; |
52 |
| - if (transformTextTemplate.status !== "available") { |
53 |
| - return; |
54 |
| - } |
55 |
| - if (PROHIBITED_ATTRIBUTES.indexOf(transformAttribute) !== -1) { |
56 |
| - console.warn(`Widget tries to change ${transformAttribute} attribute, this is prohibited`); |
57 |
| - return; |
58 |
| - } |
59 |
| - |
60 |
| - let value = transformTextTemplate.value; |
61 |
| - |
62 |
| - if (transformRemoveSpaces) { |
63 |
| - value = value.replace(/\s/g, ""); |
64 |
| - } |
| 27 | + }, []); |
65 | 28 |
|
66 |
| - if (transformTextTransform === "lowercase") { |
67 |
| - value = value.toLowerCase(); |
68 |
| - } else if (transformTextTransform === "uppercase") { |
69 |
| - value = value.toUpperCase(); |
70 |
| - } |
| 29 | + useMutationObserver({ |
| 30 | + target: bodyRef, |
| 31 | + options: { attributes: true, childList: true }, |
| 32 | + callback: handleMutations |
| 33 | + }); |
71 | 34 |
|
72 |
| - if ((transformElement === "general" && selectorSelection === "parent") || transformElement === "parent") { |
73 |
| - const selector = transformElement === "general" ? selectorParentsSelector : transformParentsSelector; |
74 |
| - this.handleParent($el, transformAttribute, value, selector); |
75 |
| - } else if ( |
76 |
| - (transformElement === "general" && selectorSelection === "sibling") || |
77 |
| - transformElement === "sibling" |
78 |
| - ) { |
79 |
| - this.handleSiblings( |
80 |
| - $el, |
81 |
| - transformAttribute, |
82 |
| - value, |
83 |
| - transformElement === "general" ? selectorSiblingFilter : transformSiblingFilter, |
84 |
| - transformElement === "general" ? selectorSiblingSubFilter : transformSiblingSubFilter |
85 |
| - ); |
86 |
| - } |
87 |
| - }); |
88 |
| - } |
| 35 | + useEffect(() => { |
| 36 | + runTransformer(); |
| 37 | + }, [elRef.current, props]); |
89 | 38 |
|
90 |
| - private handleSiblings( |
91 |
| - $el: Cash, |
92 |
| - attributeName: string, |
93 |
| - attributeValue: string, |
94 |
| - siblingFilter?: string, |
95 |
| - siblingSubFilter?: string |
96 |
| - ): void { |
97 |
| - if (!$el) { |
98 |
| - return; |
99 |
| - } |
100 |
| - const $generalSiblings = $el.siblings(siblingFilter ? siblingFilter : undefined); |
101 |
| - if ($generalSiblings.length === 0) { |
102 |
| - return; |
103 |
| - } |
104 |
| - if (typeof siblingSubFilter === "undefined" || siblingSubFilter === "") { |
105 |
| - $generalSiblings.attr(attributeName, attributeValue); |
106 |
| - } else { |
107 |
| - $generalSiblings.each(function() { |
108 |
| - $(this) |
109 |
| - .find(siblingSubFilter) |
110 |
| - .attr(attributeName, attributeValue); |
111 |
| - }); |
112 |
| - } |
113 |
| - } |
114 |
| - |
115 |
| - private handleParent($el: Cash, attributeName: string, attributeValue: string, parentSelector?: string): void { |
116 |
| - if (!$el) { |
117 |
| - return; |
118 |
| - } |
119 |
| - if (parentSelector) { |
120 |
| - const closestParent = $el.closest(parentSelector); |
121 |
| - if (closestParent.length === 1) { |
122 |
| - closestParent.attr(attributeName, attributeValue); |
123 |
| - return; |
124 |
| - } |
125 |
| - } |
126 |
| - $el.parent().attr(attributeName, attributeValue); |
127 |
| - } |
128 |
| -} |
| 39 | + return <div ref={elRef} className={"attributeHelper"} />; |
| 40 | +}; |
129 | 41 |
|
130 |
| -export default hot(AttributeHelper); |
| 42 | +export default AttributeHelper; |
0 commit comments