Skip to content
This repository has been archived by the owner on Nov 17, 2021. It is now read-only.

Commit

Permalink
refactor(Editor): Fix state bugs causing re-renders/lost configs
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-ketch committed Mar 7, 2020
1 parent e2173b0 commit efda1b8
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 169 deletions.
91 changes: 41 additions & 50 deletions src/demo/editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,45 @@
import React, { useState } from 'react'
import React from 'react'
import { getTheme } from '../utils/theme'
import { Header } from './header'
import { ThemeVariables } from './theme'
import { ThemeSwitcher } from './themeSwitcher'
import { VariableKnobs } from './variables/form'
import { ContributeForm } from './contributeModal'
import { ThemeObject } from '../utils'

const theme = getTheme()

export const ThemeEditor = (): JSX.Element => {
const [activeTheme, setTheme] = useState<string>(theme)
const [contributeModalIsOpen, setContributeModal] = useState<boolean>(false)
const [themeOverrides, setThemeOverrides] = useState<
Record<string, ThemeObject>
>({})

const openContributeModal = React.useCallback(
() => setContributeModal(true),
[]
)

const closeContributeModal = React.useCallback(
() => setContributeModal(false),
[]
)

return (
<>
<Header />

<h2 id="themeName">
<span>Theme</span>

<ThemeSwitcher activeTheme={activeTheme} onChangeTheme={setTheme} />
</h2>

<hr />

<h3>Customize</h3>

<VariableKnobs theme={activeTheme} onContribute={openContributeModal} />

{contributeModalIsOpen && (
<ContributeForm
themeOverrides={themeOverrides[activeTheme] ?? {}}
baseTheme={themeOverrides[activeTheme] ?? {}}
baseThemeName={activeTheme}
onClose={closeContributeModal}
/>
)}
</>
)

type Props = {}

interface State {
activeTheme: string
}

export class ThemeEditor extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
activeTheme: getTheme()
}
}

setTheme = (theme: string): void => {
this.setState({ activeTheme: theme })
}

render(): JSX.Element {
return (
<>
<Header />

<h2 id="themeName">
<span>Theme</span>

<ThemeSwitcher
activeTheme={this.state.activeTheme}
onChangeTheme={this.setTheme}
/>
</h2>

<h3>Customize</h3>

<ThemeVariables activeTheme={this.state.activeTheme} />
</>
)
}
}
153 changes: 153 additions & 0 deletions src/demo/editor/theme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import React from 'react'
import { getCssVariables } from '../parseCss'
import { ThemeObject } from '../utils'
import { getPreview } from '../utils/preview'
import { getThemeCSS, upsertThemeOverrides } from '../utils/theme'
import { parseQueries, removeQuery, upsertQuery } from '../utils/url'
import { ContributeForm } from './contributeModal'
import { VariableKnobs } from './variables/form'

export type ThemeConfigs = Record<string, ThemeObject>

type Props = {
activeTheme: string
}

interface State {
contributeModalIsOpen: boolean
themeOverrides: ThemeConfigs
themes: ThemeConfigs
}

export class ThemeVariables extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
contributeModalIsOpen: false,
themes: {},
themeOverrides: {}
}
}

setThemeOverrides = (
variable: string,
value: string,
commit = false
): void => {
const customizations = {
...(this.state.themeOverrides[this.props.activeTheme] ?? {})
}

if (
(this.state.themes[this.props.activeTheme] !== undefined &&
this.state.themes[this.props.activeTheme][variable]?.toLowerCase() ===
value.toLowerCase()) ||
value === ''
) {
delete customizations[variable]

if (commit === true) {
removeQuery(variable)
}
} else {
customizations[variable] = value

if (commit === true) {
upsertQuery(variable, value)
}
}

if (
JSON.stringify(customizations) !==
JSON.stringify(this.state.themeOverrides[this.props.activeTheme])
) {
upsertThemeOverrides(
this.state.themes[this.props.activeTheme],
customizations
)

this.setState({
themeOverrides: {
...this.state.themeOverrides,
[this.props.activeTheme]: customizations
}
})
}
}

openContributeModal = (): void =>
this.setState({ contributeModalIsOpen: true })

closeContributeModal = (): void =>
this.setState({ contributeModalIsOpen: false })

fetchCSS = (useQueryParams = false): void => {
const css = getThemeCSS(this.props.activeTheme)
const variables = getCssVariables(css)

this.setState(
{
...this.state,
themes: { ...this.state.themes, [this.props.activeTheme]: variables },
themeOverrides: {
...this.state.themeOverrides,
[this.props.activeTheme]: useQueryParams
? parseQueries(Object.keys(variables ?? {}))
: this.state.themeOverrides[this.props.activeTheme] ?? {}
}
},
this.injectOverrides
)
}

injectOverrides = (): void => {
upsertThemeOverrides(
this.state.themes[this.props.activeTheme],
this.state.themeOverrides[this.props.activeTheme]
)
}

componentDidMount(): void {
this.fetchCSS(true)

const preview = getPreview()
if (preview !== null) {
preview.addEventListener('load', this.injectOverrides)
}
}

componentDidUpdate(prevProps: Props): void {
if (
prevProps.activeTheme !== this.props.activeTheme &&
this.state.themes[this.props.activeTheme] === undefined
) {
this.fetchCSS()
}
}

render(): JSX.Element {
return (
<>
<VariableKnobs
theme={this.state.themes[this.props.activeTheme] ?? {}}
themeOverrides={
this.state.themeOverrides[this.props.activeTheme] ?? {}
}
onContribute={this.openContributeModal}
onChange={this.setThemeOverrides}
/>

{this.state.contributeModalIsOpen && (
<ContributeForm
baseTheme={this.state.themes[this.props.activeTheme] ?? {}}
baseThemeName={this.props.activeTheme}
themeOverrides={
this.state.themeOverrides[this.props.activeTheme] ?? {}
}
onClose={this.closeContributeModal}
/>
)}
</>
)
}
}
2 changes: 1 addition & 1 deletion src/demo/editor/variables/colorInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Snapshot = null | {
valueChanged: boolean
}

export class ColorInput extends React.Component<Props, State> {
export class ColorInput extends React.PureComponent<Props, State> {
pickrEl: React.MutableRefObject<HTMLButtonElement | null>
originalColor: Props['value']

Expand Down
Loading

0 comments on commit efda1b8

Please sign in to comment.