This repository has been archived by the owner on Dec 13, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 208
/
create-glamorous.js
175 lines (157 loc) Β· 5.42 KB
/
create-glamorous.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* This is a relatively small abstraction that's ripe for open sourcing.
* Documentation is in the README.md
*/
import React, {Component} from 'react'
import {PropTypes} from './react-compat'
import {CHANNEL} from './constants'
import withTheme from './with-theme'
import getGlamorClassName from './get-glamor-classname'
export default createGlamorous
function createGlamorous(splitProps) {
// TODO: in a breaking version, make this default to true
glamorous.config = {useDisplayNameInClassName: false}
return glamorous
/**
* This is the main export and the function that people
* interact with most directly.
*
* It accepts a component which can be a string or
* a React Component and returns
* a "glamorousComponentFactory"
* @param {String|ReactComponent} comp the component to render
* @param {Object} options helpful info for the GlamorousComponents
* @return {Function} the glamorousComponentFactory
*/
function glamorous(comp, {rootEl, displayName, forwardProps = []} = {}) {
return glamorousComponentFactory
/**
* This returns a React Component that renders the comp (closure)
* with a className based on the given glamor styles object(s)
* @param {...Object|Function} styles the styles to create with glamor.
* If any of these are functions, they are invoked with the component
* props and the return value is used.
* @return {ReactComponent} the ReactComponent function
*/
function glamorousComponentFactory(...styles) {
/**
* This is a component which will render the comp (closure)
* with the glamorous styles (closure). Forwards any valid
* props to the underlying component.
* @param {Object} theme the theme object
* @return {ReactElement} React.createElement
*/
const GlamorousComponent = withTheme(function BaseGlamorousComponent(
props,
context,
) {
const {toForward, cssOverrides} = splitProps(props, GlamorousComponent)
// freeze the theme object in dev mode
const theme = process.env.NODE_ENV === 'production' ?
props.theme :
Object.freeze(props.theme)
// create className to apply
const fullClassName = getGlamorClassName(
GlamorousComponent.styles,
props,
cssOverrides,
theme,
context,
)
const debugClassName = glamorous.config.useDisplayNameInClassName ?
cleanClassname(GlamorousComponent.displayName) :
''
const className = `${fullClassName} ${debugClassName}`.trim()
return React.createElement(GlamorousComponent.comp, {
ref: props.innerRef,
...toForward,
className,
})
}, {noWarn: true})
GlamorousComponent.propTypes = {
className: PropTypes.string,
cssOverrides: PropTypes.object,
theme: PropTypes.object,
innerRef: PropTypes.func,
glam: PropTypes.object,
}
const defaultContextTypes = {
[CHANNEL]: PropTypes.object,
}
let userDefinedContextTypes = null
// configure the contextTypes to be settable by the user,
// however also retaining the glamorous channel.
Object.defineProperty(GlamorousComponent, 'contextTypes', {
enumerable: true,
configurable: true,
set(value) {
userDefinedContextTypes = value
},
get() {
// if the user has provided a contextTypes definition,
// merge the default context types with the provided ones.
if (userDefinedContextTypes) {
return {
...defaultContextTypes,
...userDefinedContextTypes,
}
}
return defaultContextTypes
},
})
function withComponent(newComp, options = {}) {
return glamorous(newComp, {
forwardProps: GlamorousComponent.forwardProps,
...options,
})(GlamorousComponent.styles)
}
Object.assign(
GlamorousComponent,
getGlamorousComponentMetadata({
comp,
styles,
rootEl,
forwardProps,
displayName,
}),
{withComponent},
)
return GlamorousComponent
}
}
function getGlamorousComponentMetadata({
comp,
styles,
rootEl,
forwardProps,
displayName,
}) {
const componentsComp = comp.comp ? comp.comp : comp
return {
// join styles together (for anyone doing: glamorous(glamorous.a({}), {}))
styles: when(comp.styles, styles),
// keep track of the ultimate rootEl to render (we never
// actually render anything but
// the base component, even when people wrap a glamorous
// component in glamorous
comp: componentsComp,
rootEl: rootEl || componentsComp,
// join forwardProps (for anyone doing: glamorous(glamorous.a({}), {}))
forwardProps: when(comp.forwardProps, forwardProps),
// set the displayName to something that's slightly more
// helpful than `GlamorousComponent` :)
displayName: displayName || `glamorous(${getDisplayName(comp)})`,
}
}
function when(comp, prop) {
return comp ? comp.concat(prop) : prop
}
function getDisplayName(comp) {
return typeof comp === 'string' ?
comp :
comp.displayName || comp.name || 'unknown'
}
}
function cleanClassname(className) {
return className.replace(/ /g, '-').replace(/[^A-Za-z0-9\-_]/g, '_')
}