|
| 1 | +import React, { Component } from 'react' |
| 2 | +import PropTypes from 'prop-types' |
| 3 | +import classnames from 'classnames' |
| 4 | + |
| 5 | +import { defaultOptions } from "react-nice-avatar/utils" |
| 6 | +import Face from "react-nice-avatar/face/index" |
| 7 | +import Hair from "react-nice-avatar/hair/index" |
| 8 | +import Hat from "react-nice-avatar/hat/index" |
| 9 | +import Eyes from "react-nice-avatar/eyes/index" |
| 10 | +import Glasses from "react-nice-avatar/glasses/index" |
| 11 | +import Ear from "react-nice-avatar/ear/index" |
| 12 | +import Nose from "react-nice-avatar/nose/index" |
| 13 | +import Mouth from "react-nice-avatar/mouth/index" |
| 14 | +import Shirt from "react-nice-avatar/shirt/index" |
| 15 | + |
| 16 | +import SectionWrapper from "./SectionWrapper/index" |
| 17 | + |
| 18 | +import './index.scss' |
| 19 | + |
| 20 | +export default class AvatarEditor extends Component { |
| 21 | + static propTypes = { |
| 22 | + config: PropTypes.object.isRequired, |
| 23 | + shape: PropTypes.string.isRequired, |
| 24 | + updateConfig: PropTypes.func.isRequired, |
| 25 | + updateShape: PropTypes.func.isRequired, |
| 26 | + download: PropTypes.func.isRequired |
| 27 | + } |
| 28 | + |
| 29 | + constructor (props) { |
| 30 | + super(props) |
| 31 | + this.state = { |
| 32 | + isCodeShow: false |
| 33 | + } |
| 34 | + this.myDefaultOptions = this.genDefaultOptions(defaultOptions) |
| 35 | + this.shapes = ['circle', 'rounded', 'square'] |
| 36 | + } |
| 37 | + |
| 38 | + // Modification on defaultOptions for convenient |
| 39 | + genDefaultOptions (opts) { |
| 40 | + const hairSet = new Set(opts.hairStyleMan.concat(opts.hairStyleWoman)) |
| 41 | + return { |
| 42 | + ...opts, |
| 43 | + hairStyle: Array.from(hairSet) |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + switchConfig (type, currentOpt) { |
| 48 | + const { updateConfig } = this.props |
| 49 | + const opts = this.myDefaultOptions[type] |
| 50 | + const currentIdx = opts.findIndex(item => item === currentOpt) |
| 51 | + const newIdx = (currentIdx + 1) % opts.length |
| 52 | + updateConfig(type, opts[newIdx]) |
| 53 | + } |
| 54 | + |
| 55 | + switchShape (currentShape) { |
| 56 | + const { updateShape } = this.props |
| 57 | + const currentIdx = this.shapes.findIndex(item => item === currentShape) |
| 58 | + const newIdx = (currentIdx + 1) % this.shapes.length |
| 59 | + updateShape(this.shapes[newIdx]) |
| 60 | + } |
| 61 | + |
| 62 | + toggleCodeShow () { |
| 63 | + const { isCodeShow } = this.state |
| 64 | + this.setState({ |
| 65 | + isCodeShow: !isCodeShow |
| 66 | + }) |
| 67 | + } |
| 68 | + |
| 69 | + genCodeString (config) { |
| 70 | + return "const config = " + |
| 71 | + JSON.stringify(config, null, 2) + |
| 72 | + "\n" + |
| 73 | + "const myConfig = genConfig(config)\n" + |
| 74 | + "<NiceAvatar style={{ width: '5rem', height: '5rem' }} {...myConfig} />" |
| 75 | + } |
| 76 | + |
| 77 | + render () { |
| 78 | + const { config, shape, download } = this.props |
| 79 | + const { isCodeShow } = this.state |
| 80 | + return ( |
| 81 | + <div className="AvatarEditor rounded-full p-3 flex items-center"> |
| 82 | + {/* Face */} |
| 83 | + <SectionWrapper |
| 84 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 85 | + tip="Face" |
| 86 | + switchConfig={this.switchConfig.bind(this, 'faceColor', config.faceColor)}> |
| 87 | + <Face color={config.faceColor} /> |
| 88 | + </SectionWrapper> |
| 89 | + {/* Hair style */} |
| 90 | + <SectionWrapper |
| 91 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 92 | + tip="Hair" |
| 93 | + switchConfig={this.switchConfig.bind(this, 'hairStyle', config.hairStyle)}> |
| 94 | + <Hair style={config.hairStyle} color="#fff" colorRandom /> |
| 95 | + </SectionWrapper> |
| 96 | + {/* Hat style */} |
| 97 | + <SectionWrapper |
| 98 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 99 | + tip="Hat" |
| 100 | + switchConfig={this.switchConfig.bind(this, 'hatStyle', config.hatStyle)}> |
| 101 | + <Hat style={config.hatStyle} color="#fff" /> |
| 102 | + </SectionWrapper> |
| 103 | + {/* Eyes style */} |
| 104 | + <SectionWrapper |
| 105 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 106 | + tip="Eyes" |
| 107 | + switchConfig={this.switchConfig.bind(this, 'eyeStyle', config.eyeStyle)}> |
| 108 | + <Eyes style={config.eyeStyle} color="#fff" /> |
| 109 | + </SectionWrapper> |
| 110 | + {/* Glasses style */} |
| 111 | + <SectionWrapper |
| 112 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 113 | + tip="Glasses" |
| 114 | + switchConfig={this.switchConfig.bind(this, 'glassesStyle', config.glassesStyle)}> |
| 115 | + <Glasses style={config.glassesStyle} color="#fff" /> |
| 116 | + </SectionWrapper> |
| 117 | + {/* Ear style */} |
| 118 | + <SectionWrapper |
| 119 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 120 | + tip="Ear" |
| 121 | + switchConfig={this.switchConfig.bind(this, 'earSize', config.earSize)}> |
| 122 | + <Ear size={config.earSize} color="#fff" /> |
| 123 | + </SectionWrapper> |
| 124 | + {/* Nose style */} |
| 125 | + <SectionWrapper |
| 126 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 127 | + tip="Nose" |
| 128 | + switchConfig={this.switchConfig.bind(this, 'noseStyle', config.noseStyle)}> |
| 129 | + <Nose style={config.noseStyle} color="#fff" /> |
| 130 | + </SectionWrapper> |
| 131 | + {/* Mouth style */} |
| 132 | + <SectionWrapper |
| 133 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 134 | + tip="Mouth" |
| 135 | + switchConfig={this.switchConfig.bind(this, 'mouthStyle', config.mouthStyle)}> |
| 136 | + <Mouth style={config.mouthStyle} color="#fff" /> |
| 137 | + </SectionWrapper> |
| 138 | + {/* Shirt style */} |
| 139 | + <SectionWrapper |
| 140 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 141 | + tip="Shirt" |
| 142 | + switchConfig={this.switchConfig.bind(this, 'shirtStyle', config.shirtStyle)}> |
| 143 | + <Shirt style={config.shirtStyle} color="#fff" /> |
| 144 | + </SectionWrapper> |
| 145 | + |
| 146 | + {/* Shape style */} |
| 147 | + <SectionWrapper |
| 148 | + className="w-8 h-8 rounded-full p-2 mx-2" |
| 149 | + tip="Shape" |
| 150 | + switchConfig={this.switchShape.bind(this, shape)}> |
| 151 | + <div |
| 152 | + className={classnames("w-3 h-3 bg-white", { |
| 153 | + "rounded-full": shape === 'circle', |
| 154 | + "rounded": shape === 'rounded' |
| 155 | + })} /> |
| 156 | + </SectionWrapper> |
| 157 | + |
| 158 | + <div className="divider w-0.5 h-5 rounded mx-2" /> |
| 159 | + <div className="mx-2 relative flex justify-center"> |
| 160 | + <i |
| 161 | + className={classnames("iconfont icon-code text-xl cursor-pointer transition duration-300 hover:text-green-100", { |
| 162 | + banTip: isCodeShow |
| 163 | + })} |
| 164 | + data-tip="Config" |
| 165 | + onClick={this.toggleCodeShow.bind(this)} /> |
| 166 | + <div className={classnames("rounded-lg bg-white py-5 px-6 absolute bottom-full codeBlock mb-4", { |
| 167 | + active: isCodeShow |
| 168 | + })}> |
| 169 | + <pre className="text-sm">{this.genCodeString(config)}</pre> |
| 170 | + </div> |
| 171 | + </div> |
| 172 | + |
| 173 | + <div className="divider w-0.5 h-5 rounded mx-2" /> |
| 174 | + <i |
| 175 | + className="iconfont icon-download text-xl mx-2 cursor-pointer transition duration-300 hover:text-green-100" |
| 176 | + data-tip="Download" |
| 177 | + onClick={download} /> |
| 178 | + </div> |
| 179 | + ) |
| 180 | + } |
| 181 | +} |
0 commit comments