-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdiv.js
97 lines (83 loc) · 3.19 KB
/
div.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import $ from '../utils/dom-render-svg'
import { parse as parseGradient } from 'gradient-parser'
const kebabToCamel = s => s.replace(/-./g, x => x[1].toUpperCase())
function isTransparent (color) {
if (!color || color === 'none' || color === 'transparent') return true
if (color.startsWith('rgba')) {
const rgba = color.match(/[\d.]+/g)
if (rgba[3] === '0') return true
}
return false
}
export default ({
debug,
fonts
}) => async (element, { x, y, width, height, style }) => {
if (!width || !height) return
const backgroundColor = style.getPropertyValue('background-color')
const backgroundImage = style.getPropertyValue('background-image') ?? 'none'
// Skip visually empty blocks
if (isTransparent(backgroundColor) && isTransparent(backgroundImage)) return
const borderColor = style.getPropertyValue('border-color')
const borderStyle = style.getPropertyValue('border-style')
const props = {
x,
y,
width,
height,
...((() => {
// TODO SVG stroke is drawn on center, CSS stroke is drawn on outside
// TODO border-top|bottom|left|right
// TODO stroke-style
if (borderStyle === 'none') return
if (isTransparent(borderColor)) return
return {
stroke: borderColor ?? 'none',
'stroke-width': style.getPropertyValue('border-width')
}
})() ?? {}),
fill: backgroundColor,
rx: parseInt(style.getPropertyValue('border-radius')) || null
}
return isTransparent(backgroundImage)
? $('rect', props)
// Render <rect> with background image
: (() => {
const defs = $('defs')
const id = `gradient_${Date.now()}-${(Math.random() * 46656) | 0}`
// TODO handle multiple gradients
const { colorStops, orientation, type } = parseGradient(backgroundImage)?.[0] ?? {}
// TODO handle repeating gradients type, SEE https://github.com/rafaelcaricio/gradient-parser?tab=readme-ov-file#ast
const gradient = $(kebabToCamel(type), {
id,
gradientUnits: 'objectBoundingBox', // Allow specifying rotation center in %
gradientTransform: orientation
? (() => {
switch (orientation.type) {
case 'angular': return `rotate(${270 + parseFloat(orientation.value)}, 0.5, 0.5)`
case 'directional': {
switch (orientation.value) {
case 'top': return 'rotate(270, 0.5, 0.5)'
case 'right': return null
case 'bottom': return 'rotate(90, 0.5, 0.5)'
case 'left': return 'rotate(180, 0.5, 0.5)'
}
}
}
})()
: 'rotate(90, 0.5, 0.5)'
}, defs, colorStops.map((colorStop, index) => (
$('stop', {
offset: colorStop.length
// TODO handle colorStop.length.type other than '%'
? +colorStop.length.value / 100
: index / (colorStops.length - 1),
'stop-color': `${colorStop.type}(${colorStop.value})`
}, gradient)
)))
return $('g', {}, null, [
defs,
$('rect', { ...props, fill: `url(#${id})` })
])
})()
}