@ -1,16 +1,18 @@
import React from 'react'
import React from 'react'
import enhanceWithClickOutside from 'react-click-outside'
import enhanceWithClickOutside from 'react-click-outside'
import omitBy from 'lodash.omitby'
import ThemeSelect from './ThemeSelect'
import ThemeSelect from './ThemeSelect'
import FontSelect from './FontSelect'
import FontSelect from './FontSelect'
import Slider from './Slider'
import Slider from './Slider'
import Toggle from './Toggle'
import Toggle from './Toggle'
import WindowPointer from './WindowPointer'
import WindowPointer from './WindowPointer'
import { COLORS } from '../lib/constants'
import { COLORS , DEFAULT _PRESETS } from '../lib/constants'
import { getPresets , savePresets } from '../lib/util'
import { toggle } from '../lib/util'
import { toggle } from '../lib/util'
import SettingsIcon from './svg/Settings'
import SettingsIcon from './svg/Settings'
import * as Arrows from './svg/Arrows'
import * as Arrows from './svg/Arrows'
//import Remove from './svg/Remove'
import Remove from './svg/Remove'
const WindowSettings = React . memo (
const WindowSettings = React . memo (
( {
( {
@ -202,31 +204,122 @@ const MenuButton = React.memo(({ name, select, selected }) => {
)
)
} )
} )
/ * c o n s t P r e s e t s = R e a c t . m e m o ( ( { s h o w , p r e s e t s , t o g g l e , c r e a t e , r e m o v e } ) = > {
const Preset = React . memo ( ( { remove , apply , selected , preset } ) => (
< div className = "preset-container" >
< button
className = "preset-tile"
onClick = { ( ) => apply ( preset ) }
disabled = { preset . id === selected }
/ >
{ preset . custom ? (
< button className = "preset-remove" onClick = { ( ) => remove ( preset . id ) } >
< Remove / >
< / b u t t o n >
) : null }
< style jsx >
{ `
button {
outline : none ;
border : none ;
background : transparent ;
cursor : pointer ;
padding : 0 ;
}
. preset - container {
display : flex ;
position : relative ;
flex : 0 0 96 px ;
height : 96 px ;
margin - right : 8 px ;
}
. preset - tile {
flex : 1 ;
border - radius : 3 px ;
background - repeat : no - repeat ;
background - position : center center ;
background - size : contain ;
cursor : $ { preset . id === selected ? 'initial' : 'pointer' } ;
background - image : $ { ` url(' ${ preset . icon } ') ` } ;
background - color : $ { preset . icon ? 'initial' : preset . backgroundColor } ;
box - shadow : $ { preset . id === selected
? ` inset 0px 0px 0px 2px ${ COLORS . SECONDARY } `
: 'initial' } ;
}
. preset - remove {
display : flex ;
align - items : center ;
justify - content : center ;
position : absolute ;
padding : 0 ;
top : 6 px ;
right : 6 px ;
width : 11 px ;
height : 11 px ;
border - radius : 999 px ;
background - color : $ { COLORS . SECONDARY } ;
}
` }
< / s t y l e >
< / d i v >
) )
const Presets = React . memo (
( { show , create , toggle , undo , presets , selected , remove , apply , applied , contentRef } ) => {
const customPresetsLength = presets . length - DEFAULT _PRESETS . length
const disabledCreate = selected != null
return (
return (
< div className = "settings-presets" >
< div className = "settings-presets" >
< div className = "settings-presets-header" >
< div className = "settings-presets-header" >
< span > Presets < / s p a n >
< span > Presets < / s p a n >
{ show && < button className = "settings-presets-create" onClick = { create } > create + < / b u t t o n > }
{ show && (
< button className = "settings-presets-create" onClick = { create } disabled = { disabledCreate } >
create +
< / b u t t o n >
) }
< button className = "settings-presets-arrow" onClick = { toggle } >
< button className = "settings-presets-arrow" onClick = { toggle } >
{ show ? < Arrows . Up / > : < Arrows . Down / > }
{ show ? < Arrows . Up / > : < Arrows . Down / > }
< / b u t t o n >
< / b u t t o n >
< / d i v >
< / d i v >
{ show && (
{ show ? (
< div className = "settings-presets-content" >
< div className = "settings-presets-content" ref = { contentRef } >
{ presets . map ( ( { id , backgroundColor , userCreated } ) => (
{ presets . filter ( p => p . custom ) . map ( preset => (
< div key = { id } className = "settings-presets-preset" style = { {
< Preset
backgroundColor
key = { preset . id }
} } >
remove = { remove }
{
apply = { apply }
userCreated ? < button className = "settings-presets-remove" onClick = { ( ) => remove ( id ) } > < Remove / > < / b u t t o n > : n u l l
preset = { preset }
}
selected = { selected }
< / d i v >
/ >
) ) }
{ customPresetsLength > 0 ? < div className = "settings-presets-divider" / > : null }
{ presets . filter ( p => ! p . custom ) . map ( preset => (
< Preset key = { preset . id } apply = { apply } preset = { preset } selected = { selected } / >
) ) }
) ) }
< / d i v >
< / d i v >
) }
) : null }
{ show && applied ? (
< div className = "settings-presets-applied" >
< span > Preset applied ! < / s p a n >
< button onClick = { undo } >
undo < span > & # x21A9 ; < / s p a n >
< / b u t t o n >
< / d i v >
) : null }
< style jsx >
< style jsx >
{ `
{ `
button {
outline : none ;
border : none ;
background : transparent ;
cursor : pointer ;
padding : 0 ;
}
. settings - presets {
. settings - presets {
border - bottom : 1 px solid $ { COLORS . SECONDARY } ;
border - bottom : 1 px solid $ { COLORS . SECONDARY } ;
}
}
@ -240,7 +333,13 @@ const MenuButton = React.memo(({ name, select, selected }) => {
align - items : center ;
align - items : center ;
}
}
. settings - presets - arrow , . settings - presets - create , . settings - presets - remove {
. settings - presets - header > span {
font - size : 14 px ;
}
. settings - presets - arrow ,
. settings - presets - create ,
. settings - presets - remove {
cursor : pointer ;
cursor : pointer ;
background : transparent ;
background : transparent ;
outline : none ;
outline : none ;
@ -251,9 +350,15 @@ const MenuButton = React.memo(({ name, select, selected }) => {
. settings - presets - create {
. settings - presets - create {
color : $ { COLORS . GRAY } ;
color : $ { COLORS . GRAY } ;
padding : 0 8 px ;
padding : 0 8 px ;
cursor : $ { disabledCreate ? 'not-allowed' : 'pointer' } ;
}
. settings - presets - create : enabled : hover {
color : $ { COLORS . SECONDARY } ;
}
}
. settings - presets - arrow {
. settings - presets - arrow {
display : flex ;
position : absolute ;
position : absolute ;
right : 16 px ;
right : 16 px ;
}
}
@ -261,41 +366,61 @@ const MenuButton = React.memo(({ name, select, selected }) => {
. settings - presets - content {
. settings - presets - content {
display : flex ;
display : flex ;
overflow - x : scroll ;
overflow - x : scroll ;
margin : 12 px 8 px ;
margin : 0 8 px 12 px 8 px ;
align - items : center ;
/* https://iamsteve.me/blog/entry/using-flexbox-for-horizontal-scrolling-navigation */
flex - wrap : nowrap ;
- webkit - overflow - scrolling : touch ;
}
}
. settings - presets - preset {
. settings - presets - divider {
height : 72 px ;
padding : 1 px ;
border - radius : 3 px ;
border - radius : 3 px ;
height : 96 px ;
margin - right : 8 px ;
margin - right : 8 px ;
flex : 0 0 96 px ;
background - color : $ { COLORS . DARK _GRAY } ;
position : relative ;
}
}
. settings - presets - remove {
. settings - presets - applied {
display : flex ;
display : flex ;
align - items : center ;
justify - content : space - between ;
justify - content : center ;
position : absolute ;
padding : 0 ;
top : 6 px ;
right : 6 px ;
width : 11 px ;
height : 11 px ;
border - radius : 999 px ;
background - color : $ { COLORS . SECONDARY } ;
background - color : $ { COLORS . SECONDARY } ;
width : 100 % ;
color : $ { COLORS . BLACK } ;
padding : 4 px 8 px ;
}
. settings - presets - applied button {
float : right ;
}
. settings - presets - applied button span {
float : right ;
margin : 1 px 0 0 2 px ;
}
}
` }
` }
< / s t y l e >
< / s t y l e >
< / d i v >
< / d i v >
)
)
} ) * /
}
)
class Settings extends React . PureComponent {
class Settings extends React . PureComponent {
state = {
state = {
presets : DEFAULT _PRESETS ,
isVisible : false ,
isVisible : false ,
selectedMenu : 'Window' ,
selectedMenu : 'Window' ,
showPresets : false
showPresets : false ,
previousSettings : null
}
presetContentRef = React . createRef ( )
componentDidMount ( ) {
const storedPresets = getPresets ( localStorage ) || [ ]
this . setState ( ( { presets } ) => ( {
presets : [ ... storedPresets , ... presets ]
} ) )
}
}
toggleVisible = ( ) => this . setState ( toggle ( 'isVisible' ) )
toggleVisible = ( ) => this . setState ( toggle ( 'isVisible' ) )
@ -311,7 +436,7 @@ class Settings extends React.PureComponent {
case 'Window' :
case 'Window' :
return (
return (
< WindowSettings
< WindowSettings
onChange = { this . props. on Change}
onChange = { this . handle Change}
windowTheme = { this . props . windowTheme }
windowTheme = { this . props . windowTheme }
paddingHorizontal = { this . props . paddingHorizontal }
paddingHorizontal = { this . props . paddingHorizontal }
paddingVertical = { this . props . paddingVertical }
paddingVertical = { this . props . paddingVertical }
@ -327,21 +452,90 @@ class Settings extends React.PureComponent {
case 'Type' :
case 'Type' :
return (
return (
< TypeSettings
< TypeSettings
onChange = { this . props. on Change}
onChange = { this . handle Change}
font = { this . props . fontFamily }
font = { this . props . fontFamily }
size = { this . props . fontSize }
size = { this . props . fontSize }
lineHeight = { this . props . lineHeight }
lineHeight = { this . props . lineHeight }
/ >
/ >
)
)
case 'Misc' :
case 'Misc' :
return < MiscSettings format = { this . props . format } reset = { this . props. resetDefaultSettings } / >
return < MiscSettings format = { this . props . format } reset = { this . handleReset } / >
default :
default :
return null
return null
}
}
}
}
handleChange = ( key , value ) => {
this . props . onChange ( key , value )
this . setState ( { previousSettings : null } )
}
handleReset = ( ) => {
this . props . resetDefaultSettings ( )
this . setState ( { previousSettings : null } )
}
getSettingsFromProps = ( ) =>
omitBy ( this . props , ( v , k ) => typeof v === 'function' || k === 'preset' )
applyPreset = preset => {
const previousSettings = this . getSettingsFromProps ( )
this . props . applyPreset ( preset )
// TODO: this is a hack to prevent the scrollLeft position from changing when preset is applied
const { scrollLeft : previousScrollLeft } = this . presetContentRef . current
this . setState ( { previousSettings } , ( ) => {
this . presetContentRef . current . scrollLeft = previousScrollLeft
} )
}
undoPreset = ( ) => {
this . props . applyPreset ( { ... this . state . previousSettings , id : null } )
this . setState ( { previousSettings : null } )
}
removePreset = id => {
if ( this . props . preset === id ) {
this . props . onChange ( 'preset' , null )
this . setState ( { previousSettings : null } )
}
this . setState (
( { presets } ) => ( { presets : presets . filter ( p => p . id !== id ) } ) ,
this . savePresets
)
}
createPreset = async ( ) => {
const newPreset = this . getSettingsFromProps ( )
newPreset . id = ` preset: ${ Math . random ( )
. toString ( 36 )
. slice ( 2 ) } `
newPreset . custom = true
newPreset . icon = await this . props . getCarbonImage ( {
format : 'png' ,
squared : true ,
exportSize : 1
} )
this . props . onChange ( 'preset' , newPreset . id )
this . setState (
( { presets } ) => ( {
previousSettings : null ,
presets : [ newPreset , ... presets ]
} ) ,
this . savePresets
)
}
savePresets = ( ) => savePresets ( localStorage , this . state . presets . filter ( p => p . custom ) )
render ( ) {
render ( ) {
const { isVisible , selectedMenu } = this . state
const { isVisible , selectedMenu , showPresets , presets , previousSettings } = this . state
const { preset } = this . props
return (
return (
< div className = "settings-container" >
< div className = "settings-container" >
@ -353,6 +547,18 @@ class Settings extends React.PureComponent {
< / d i v >
< / d i v >
< div className = "settings-settings" >
< div className = "settings-settings" >
< WindowPointer fromLeft = "15px" / >
< WindowPointer fromLeft = "15px" / >
< Presets
show = { showPresets }
presets = { presets }
selected = { preset }
toggle = { this . togglePresets }
apply = { this . applyPreset }
undo = { this . undoPreset }
remove = { this . removePreset }
create = { this . createPreset }
applied = { ! ! previousSettings }
contentRef = { this . presetContentRef }
/ >
< div className = "settings-bottom" >
< div className = "settings-bottom" >
< div className = "settings-menu" >
< div className = "settings-menu" >
< MenuButton name = "Window" select = { this . selectMenu } selected = { selectedMenu } / >
< MenuButton name = "Window" select = { this . selectMenu } selected = { selectedMenu } / >
@ -408,7 +614,7 @@ class Settings extends React.PureComponent {
top : 52 px ;
top : 52 px ;
left : 0 ;
left : 0 ;
border : 2 px solid $ { COLORS . SECONDARY } ;
border : 2 px solid $ { COLORS . SECONDARY } ;
width : 32 0 px ;
width : 32 4 px ;
border - radius : 3 px ;
border - radius : 3 px ;
background : $ { COLORS . BLACK } ;
background : $ { COLORS . BLACK } ;
}
}