|
1 | 1 | import React, {useRef, useEffect, useState} from 'react'; |
2 | | -import getStyle from "./styles-extractor"; |
3 | 2 | import './getMatchedCSSRules-polyfill' |
4 | | -import {getWidth, getColor} from "./css-utils"; |
| 3 | +import updateStates from "./updateStates"; |
| 4 | +import ShadowRoot from "./react-shadow-dom"; |
5 | 5 |
|
6 | 6 | export default function RoundDiv({clip, style, children, ...props}) { |
7 | 7 | const [height, setHeight] = useState(0) |
8 | 8 | const [width, setWidth] = useState(0) |
9 | | - const [offsetX, setOffsetX] = useState(0) |
10 | | - const [offsetY, setOffsetY] = useState(0) |
11 | 9 | const [radius, setRadius] = useState(0) |
12 | 10 | const [background, setBackground] = useState('transparent') |
| 11 | + const [backgroundOpacity, setBackgroundOpacity] = useState(0) |
13 | 12 | const [borderColor, setBorderColor] = useState('transparent') |
14 | 13 | const [borderWidth, setBorderWidth] = useState('1') |
| 14 | + const [borderOpacity, setBorderOpacity] = useState(1) |
15 | 15 |
|
16 | 16 | const div = useRef() |
17 | 17 |
|
18 | 18 | useEffect(() => { |
19 | 19 | // attach shadow root to div |
20 | | - if (div.current?.shadowRoot) return |
21 | | - const shadow = div.current?.attachShadow({mode: 'open'}) |
22 | | - const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') |
23 | | - svg.style.position = 'fixed'; |
24 | | - svg.style.left = '0px'; |
25 | | - svg.style.top = '0px'; |
26 | | - svg.style.height = '0px'; |
27 | | - svg.style.width = '0px'; |
28 | | - svg.style.overflow = 'visible'; |
29 | | - svg.style.zIndex = '-1'; |
30 | | - const path = document.createElementNS('http://www.w3.org/2000/svg', 'path') |
31 | | - svg.appendChild(path) |
32 | | - shadow.appendChild(svg) |
33 | | - const content = document.createElement('slot') |
34 | | - shadow.appendChild(content) |
| 20 | + if (!div.current?.shadowRoot) |
| 21 | + div.current?.attachShadow({mode: 'open'}) |
35 | 22 | }, []) |
36 | 23 |
|
37 | | - useEffect(() => { |
38 | | - const boundingClientRect = div.current?.getBoundingClientRect() |
39 | | - if (boundingClientRect) { |
40 | | - setHeight(boundingClientRect.height) |
41 | | - setWidth(boundingClientRect.width) |
42 | | - setOffsetX(boundingClientRect.left) |
43 | | - setOffsetY(boundingClientRect.top) |
44 | | - } |
45 | | - const divStyle = boundingClientRect ? window?.getComputedStyle(div.current) : null |
46 | | - if (divStyle) { |
47 | | - setRadius(Number(divStyle.borderRadius.replace('px', ''))) |
48 | | - setBackground( |
49 | | - style?.background |
50 | | - || style?.backgroundColor |
51 | | - || getStyle('background', div.current)?.overwritten[1]?.value |
52 | | - || 'transparent' |
53 | | - ) |
54 | | - setBorderColor( |
55 | | - getColor(style?.border) |
56 | | - || style?.borderColor |
57 | | - || getStyle('borderColor', div.current)?.overwritten[1]?.value |
58 | | - || 'transparent' |
59 | | - ) |
60 | | - setBorderWidth( |
61 | | - getWidth(style?.border) |
62 | | - || style?.borderWidth |
63 | | - || getStyle('borderWidth', div.current)?.overwritten[1]?.value |
64 | | - || '1' |
65 | | - ) |
66 | | - } |
67 | | - }, [div, clip, style]) |
68 | | - |
69 | | - useEffect(() => { |
70 | | - const path = div.current?.shadowRoot?.querySelector('path') |
71 | | - if (!path) return |
72 | | - path.parentNode.style.width = width |
73 | | - path.parentNode.style.height = height |
74 | | - path.parentNode.style.top = offsetY |
75 | | - path.parentNode.style.left = offsetX |
76 | | - path.parentNode.removeAttributeNS('http://www.w3.org/2000/svg', 'viewBox') |
77 | | - path.parentNode.setAttributeNS( |
78 | | - 'http://www.w3.org/2000/svg', |
79 | | - 'viewBox', |
80 | | - `0 0 ${height} ${width}` |
81 | | - ) |
82 | | - const newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path') |
83 | | - newPath.setAttributeNS( |
84 | | - 'http://www.w3.org/2000/svg', |
85 | | - 'd', |
86 | | - generateSvgSquircle(height, width, radius, clip) |
87 | | - ) |
88 | | - newPath.setAttributeNS('http://www.w3.org/2000/svg', 'fill', background) |
89 | | - newPath.setAttributeNS('http://www.w3.org/2000/svg', 'stroke', borderColor) |
90 | | - newPath.setAttributeNS('http://www.w3.org/2000/svg', 'stroke-width', borderWidth) |
91 | | - // rerender |
92 | | - path.parentNode.innerHTML = newPath.outerHTML |
93 | | - }, [background, height, width, radius, clip, offsetX, offsetY, borderColor, borderWidth]) |
| 24 | + useEffect(() => updateStates({ |
| 25 | + div, |
| 26 | + style, |
| 27 | + setHeight, |
| 28 | + setWidth, |
| 29 | + setRadius, |
| 30 | + setBackground, |
| 31 | + setBackgroundOpacity, |
| 32 | + setBorderColor, |
| 33 | + setBorderWidth, |
| 34 | + setBorderOpacity |
| 35 | + }), [div, clip, style]) |
94 | 36 |
|
95 | 37 | const divStyle = { |
96 | 38 | ...style |
97 | 39 | } |
98 | 40 |
|
99 | 41 | divStyle.background = 'transparent' |
100 | | - divStyle.border = divStyle.border || '0' |
| 42 | + divStyle.borderWidth = divStyle.borderWidth || '0' |
101 | 43 | divStyle.borderColor = 'transparent' |
102 | 44 |
|
103 | 45 | return <div {...props} style={divStyle} ref={div}> |
| 46 | + <ShadowRoot> |
| 47 | + <style>{` |
| 48 | + rect { |
| 49 | + width: calc(${width}px + ${borderWidth} * 2); |
| 50 | + height: calc(${height}px + ${borderWidth} * 2); |
| 51 | + x: calc(${borderWidth} * -1); |
| 52 | + y: calc(${borderWidth} * -1); |
| 53 | + } |
| 54 | + #border { |
| 55 | + stroke-width: ${borderWidth}; |
| 56 | + } |
| 57 | + `} |
| 58 | + </style> |
| 59 | + <svg viewBox={`0 0 ${height} ${width}`} style={{ |
| 60 | + position: 'fixed', |
| 61 | + height, |
| 62 | + width, |
| 63 | + overflow: 'visible', |
| 64 | + zIndex: -1 |
| 65 | + }} xmlnsXlink="http://www.w3.org/1999/xlink"> |
| 66 | + <defs> |
| 67 | + <path d={generateSvgSquircle(height, width, radius, clip)} id="shape"/> |
| 68 | + |
| 69 | + <mask id="outsideOnly"> |
| 70 | + <rect fill="white"/> |
| 71 | + <use xlinkHref="#shape" fill="black"/> |
| 72 | + </mask> |
| 73 | + </defs> |
| 74 | + |
| 75 | + <use xlinkHref="#shape" id="border" stroke={borderColor} fill="none" |
| 76 | + opacity={borderOpacity} mask="url(#outsideOnly)"/> |
| 77 | + <use xlinkHref="#shape" fill={background} opacity={backgroundOpacity}/> |
| 78 | + </svg> |
| 79 | + <slot style={{zIndex: 1}}/> |
| 80 | + </ShadowRoot> |
104 | 81 | {children} |
105 | 82 | </div> |
106 | 83 | } |
|
0 commit comments