|
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