Repository URL to install this package:
|
Version:
4.1.3-alpha.1 ▾
|
import React from 'react'
// // import IS_PIPELINE_SERVER from 'config/env/IS_PIPELINE_SERVER'
import { EMPTY_STRING, isObj, isString, isArray } from 'exotic'
import { fliphash } from 'chain-able-boost'
// import persistance from '@skava/persistence'
import { toComponentName } from '@skava/identifier/dist/index'
//
import { fromStringToStyle } from '../deps/fromStringToStyleTag'
// import { transpileStyles } from './transpileStyles'
const persistance = new Map()
/**
* @todo - these should be in an mst store to be rehydrated?
*
* {String}
* @type {Map<StyleStringToParse, CSS>}
*/
const styleRegistry = new Map()
/**
* {Element}
* @type {Map<CSS, StyledComponent>}
*/
const styleBlockRegistry = new Map()
const globalStyleBlocks = new Set()
// @todo - set keys with fliphash
const metaRegistry = new Map()
const cssStringList = []
/**
* @see styleRegistry
* @description persistance [key, value]
*
* @param {StylesNextGen} styles next-gen styles
* @param {CSS} css transpiled
* @return {CSS} same-in-same-out
*/
function saveTranspiledStyles(styles, css) {
styleRegistry.set(styles, css)
const savedStyles = persistance.get('styles')
if (isArray(savedStyles)) {
savedStyles.push([styles, css])
persistance.set('styles', savedStyles)
}
return css
}
/**
* @todo should cache StyledComponents as well as Style
*
* @param {String | *} css
* @return {true | false | JSX.Element<CSSStyleSheet>}
*/
function getElementFromRegistry(css) {
if (css === '') {
return false
} else if (styleBlockRegistry.has(css)) {
return styleBlockRegistry.get(css)
} else {
return false
}
}
let hashed = 0
const HOT_RELOAD_STYLES = false
// let HOT_RELOAD_STYLES = !IS_PIPELINE_SERVER
// let HOT_RELOAD_STYLES = process.env.HOT === 'true'
// process.env.NODE_ENV === 'development'
// rehydrateFromPersistance()
export interface StyledEhComponentProps {
styleseh: string
className?: string
key?: string
'data-for-component': any
}
class StyledComponent extends React.Component<StyledEhComponentProps> {
// static propTypes = {
// styleseh: string.isRequired,
// // or #id
// className: string,
// // component: Object.optional,
// }
/**
* @type css only
*/
css = EMPTY_STRING
/**
* @description currently only autogenerated, see types.className
*/
selector = EMPTY_STRING
/**
* @description for persisting state on server side render
*/
state = {
css: EMPTY_STRING,
}
hasMounted: any
shouldComponentUpdate(nextProps) {
return (
HOT_RELOAD_STYLES ||
(nextProps && nextProps.styleseh !== this.props.styleseh)
)
}
// @todo
componentWillUnmount() {
// not sure if we can change props on a component...
// so we are using a meta registry
if (metaRegistry.has(this.css) === true) {
const meta = metaRegistry.get(this.css)
meta.hasMounted = false
}
}
// @todo
// 1. we render a style
// 2. we go to another page
// 3. that style was unmounted
// 4. we already rendered it
// 5. we don't show it again <- needs to go in a globals
componentDidMount() {
if (metaRegistry.has(this.css) === true) {
const meta = metaRegistry.get(this.css)
meta.hasMounted = true
}
}
// mucks with deep force
// componentDidUpdate() {
// if (HOT_RELOAD_STYLES === true) {
// this.loadStylesIfNeeded(this.props.styleseh)
// // this.forceUpdate()
// }
// }
componentWillMount() {
this.loadStylesIfNeeded(this.props.styleseh)
return Promise.resolve(this.css)
}
/**
* @description loads from registry if possible
*
* @param {String} styles styles to parse
* @return {void}
*
* @see this.handleStyles
*/
loadStylesIfNeeded(styles) {
// if state was loaded server side
if (this.css === EMPTY_STRING && this.css !== this.state.css) {
this.css = this.state.css
}
// if observable already is already set up on our instance
if (this.css !== EMPTY_STRING && !HOT_RELOAD_STYLES) {
return
}
// load from registry if we've already transpiled
if (styleRegistry.has(styles)) {
this.css = styleRegistry.get(styles)
}
// gotta load em
this.handleStyles(styles)
}
/**
* @description transpile as needed, and updates state
* @param {String} styles
* @return {void}
*/
handleStyles(styles) {
// console.dir({ handlingStyles: css })
// const css = transpileStyles(styles)
const css = styles
// save to global registry cache
saveTranspiledStyles(this.props.styleseh, css)
// set property, if observable
this.css = css
// if we can set state, do it
if (this.hasMounted || HOT_RELOAD_STYLES) {
this.setState({ css })
}
}
// componentDidUpdate() {
// if (metaRegistry.has(this.css)) {
// // meta
// const meta = Object.seal({
// hasMounted: true,
// renderCount: 0,
// hashed,
// })
// metaRegistry.set(this.css, meta)
// }
// }
/* @lint dev */
/* eslint-disable max-statements */
/**
* @see https://github.com/threepointone/glamor/blob/master/docs/server.md
* @description if HOT_RELOAD_STYLESforce reload
* @return {JSX.Element<CSSStyleSheet>}
*/
render() {
const found = getElementFromRegistry(this.css)
// if (HOT_RELOAD_STYLES) {
// hashed += 1
// const attributes = Object.seal({
// key: hashed,
// })
// const rendered = fromStringToStyle(this.css, attributes)
// styleBlockRegistry.set(this.css, rendered)
// return rendered
// }
if (found === false) {
hashed += 1
// jsx blocks
const attributes = Object.seal({
key: hashed,
})
const rendered = fromStringToStyle(this.css, attributes)
styleBlockRegistry.set(this.css, rendered)
return rendered
} else {
return found
}
}
}
let logged = false
// Array.from(stylez.metaRegistry.entries()).filter((kv) => {
// const [key, value] = kv
// // console.log({key, value})
// return value.duplicationCount > 1
// })
/**
* @todo !!!!!!!!!!!! should be loading webworker
*
* @todo - processing vs memory
* if we hash every time, more processing, less memory
* no processing...
*/
function markDuplicationIfNeeded(css, named) {
const isInStyleStrings = cssStringList.includes(css) === true
const isInStyleRegistry = styleRegistry.has(css) === true
if (metaRegistry.has(css) === true) {
const meta = metaRegistry.get(css)
if (meta.names.includes(named) === false) {
// !!! USED IN A DIFF COMPONENT !!!
meta.REQUIRES_SOLUTION = true
} else {
meta.names.push(named)
}
// if (meta.hasMounted === false) {
// meta.duplicationCount += 1
// }
meta.duplicationCount += 1
} else {
const hash = fliphash(css)
// meta
const meta = Object.seal({
REQUIRES_SOLUTION: false,
names: [named],
hasMounted: true,
renderCount: 0,
duplicationCount: 1,
hashed,
hash,
isInStyleStrings,
isInStyleRegistry,
})
metaRegistry.set(css, meta)
}
// return metaRegistry.get(css)
}
/**
* @see fromStringToStyle
* @see toComponentName
*
* @param {String} css styles to transpile as a <style> tag
* @param {React.ComponentClass} [Target=undefined]
* @return {StyledComponent}
*/
function asStyledComponent(css, Target = undefined) {
// const hash = fliphash(css)
const name = toComponentName(Target)
markDuplicationIfNeeded(css, name)
const meta = metaRegistry.get(css)
const { isInStyleRegistry, isInStyleStrings, REQUIRES_SOLUTION } = meta
/**
* @note it is pulling from registry in the render, but not hoisting
* @todo if we provide a context per page & large widget, should be easy hoist
* OR provide `namespace` in the styled.namespace
*
* @todo would also work to use for snapshots to dedupe
* @todo can use chain includesCount for how many times we have the style to more easily dedupe
*/
if (isInStyleRegistry && REQUIRES_SOLUTION === false) {
if (logged === false) {
console.warn(`
already had these styles connected.
update state with an observable,
or refresh
(should connect HMR.accept to this module)
`)
logged = true
}
// else
return ''
} else if (isInStyleStrings && REQUIRES_SOLUTION === false) {
/**
* @todo - this is when we would hoist original
*/
if (logged === false) {
console.warn(`already transpiled these styles`)
logged = true
}
globalStyleBlocks.add(css)
return ''
}
cssStringList.push(css)
// @todo - should add fromStyleToKey
const node = (
<StyledComponent key={css} styleseh={css} data-for-component={name} />
)
return node
}
// if (typeof window === 'object') {
// window.React = React
// window.stylez = {
// styleRegistry,
// styleBlockRegistry,
// globalStyleBlocks,
// metaRegistry,
// cssStringList,
// }
// }
export { StyledComponent, asStyledComponent }
export default asStyledComponent