-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathapi.js
More file actions
117 lines (98 loc) · 3.21 KB
/
Copy pathapi.js
File metadata and controls
117 lines (98 loc) · 3.21 KB
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import React from 'react'
import stylis from 'stylis'
/*
This get's used as decorator @css
Get's the template literal passed to it argument.
This contains references to props as well
Method decorators expect a function to be returned,
this function gets the parent class, name of the function -
render in this case and the descriptor for the function.
*/
let css = rawCSS => (parentClass, name, descriptor) => ({
...descriptor,
value: function() {
let originalProps
/* Totally stealing props by fake rendering the component */
let getProps = object => {
originalProps = object.props
return object
}
let rendered = descriptor.value.apply(getProps(this), arguments)
/* Replace props and return realCSS™ */
let realCSS = fillProps(rawCSS, originalProps)
/* Merge classNames */
const existingClassNames = rendered.props.className || ''
let className = `${existingClassNames} ${insertRules(realCSS)}`
/* Convert real CSS to javascripty CSS */
//let style = parseCss(realCSS);
/* Merge styles into original props */
let newProps = { ...originalProps, className }
/*
Pass on a clone of the rendered component
with our merged props.
This overrides the original render function
*/
return React.cloneElement(rendered, newProps, rendered.props.children)
}
})
/*
Replace props with actual values
Uses regex pattern to match references to props
Supports direct usage
color: {this.props.color}
Does not evaluate conditions (yet)
color: {this.props.color || 'blue'}
*/
let fillProps = (rawCSS, props) => {
rawCSS = rawCSS[0] // template literal = array
let re = /{this.props.*}/g
let matches = rawCSS.match(re)
if (matches && matches.length) {
for (let match of matches) {
let keyword = match, replaceWord, propKeys
keyword = keyword.replace('{this.props.', '')
keyword = keyword.substring(0, keyword.length - 1) // remove }
keyword = keyword.trim()
replaceWord = props
propKeys = keyword.split('.')
for (let i = 0; i < propKeys.length; i++) {
replaceWord = replaceWord[propKeys[i]]
}
rawCSS = rawCSS.replace(match, replaceWord)
}
}
return rawCSS
}
/*
Add insert rules in to css-constructor stylesheet
*/
let insertRules = realCSS => {
let style = getStyleElement()
/* Get unique classname */
let className = getHash(realCSS)
/* Convert nested CSS */
let styles = stylis(`.${className}`, realCSS)
style.innerHTML += styles
return className
}
let getHash = string => {
let hash = 0
for (let i = 0; i < string.length; i++) {
let ch = string.charCodeAt(i)
hash = (hash << 5) - hash + ch
}
/* CSS classnames should begin with an alphabet */
return 'c' + hash.toString(36)
}
let getStyleElement = () => {
let styleElement = document.querySelector('[title=css-constructor]')
if (!styleElement) {
styleElement = document.createElement('style')
styleElement.setAttribute('title', 'css-constructor')
document.head.appendChild(styleElement)
}
return styleElement
}
let camelCase = key =>
key.replace(/(\-[a-z])/g, $1 => $1.toUpperCase().replace('-', ''))
module.exports = css