|
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