mirror of
https://github.com/balzack/databag.git
synced 2025-02-14 20:49:16 +00:00
adding profile image selector
This commit is contained in:
parent
65914d9189
commit
3fdc53715a
@ -10,6 +10,7 @@
|
|||||||
"base-64": "^1.0.0",
|
"base-64": "^1.0.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-easy-crop": "^4.1.4",
|
||||||
"react-router-dom": "^6.2.2",
|
"react-router-dom": "^6.2.2",
|
||||||
"react-scripts": "5.0.0",
|
"react-scripts": "5.0.0",
|
||||||
"styled-components": "^5.3.3",
|
"styled-components": "^5.3.3",
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, useRef } from 'react'
|
||||||
import { ProfileWrapper, CloseButton } from './Profile.styled';
|
import { ProfileWrapper, CloseButton, ModalFooter, SelectButton } from './Profile.styled';
|
||||||
import { UserOutlined, CloseOutlined, EditOutlined } from '@ant-design/icons';
|
import { UserOutlined, CloseOutlined, EditOutlined } from '@ant-design/icons';
|
||||||
import { useProfile } from './useProfile.hook';
|
import { useProfile } from './useProfile.hook';
|
||||||
import { Button, Modal } from 'antd'
|
import { Button, Modal } from 'antd'
|
||||||
import { ProfileInfo } from './ProfileInfo/ProfileInfo';
|
import { ProfileInfo } from './ProfileInfo/ProfileInfo';
|
||||||
|
import { ProfileImage } from './ProfileImage/ProfileImage';
|
||||||
|
|
||||||
export function Profile(props) {
|
export function Profile(props) {
|
||||||
|
|
||||||
|
const [ logoVisible, setLogoVisible ] = useState(false);
|
||||||
const [ infoVisible, setInfoVisible ] = useState(false);
|
const [ infoVisible, setInfoVisible ] = useState(false);
|
||||||
const { state, actions } = useProfile();
|
const { state, actions } = useProfile();
|
||||||
|
const imageFile = useRef(null)
|
||||||
|
|
||||||
const Logo = () => {
|
const Logo = () => {
|
||||||
if (state.imageUrl != null) {
|
if (state.imageUrl != null) {
|
||||||
if (state.imageUrl === '') {
|
if (state.imageUrl === '') {
|
||||||
return <div class="logo"><UserOutlined /></div>
|
return <div class="logo"><UserOutlined /></div>
|
||||||
}
|
}
|
||||||
|
return <img class="logo" src={ state.imageUrl } alt="" />
|
||||||
}
|
}
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
@ -46,6 +50,29 @@ export function Profile(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onSelectImage = () => {
|
||||||
|
imageFile.current.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const selected = (e) => {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
actions.setModalImage(reader.result);
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(e.target.files[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Footer = (
|
||||||
|
<ModalFooter>
|
||||||
|
<input type='file' id='file' ref={imageFile} onChange={e => selected(e)} style={{display: 'none'}}/>
|
||||||
|
<div class="select">
|
||||||
|
<Button key="select" class="select" onClick={() => onSelectImage()}>Select Image</Button>
|
||||||
|
</div>
|
||||||
|
<Button key="select" onClick={() => setLogoVisible(false)}>Cancel</Button>
|
||||||
|
<Button key="save" type="primary" onClick={() => setLogoVisible(false)}>Save</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfileWrapper>
|
<ProfileWrapper>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
@ -53,8 +80,8 @@ export function Profile(props) {
|
|||||||
<CloseButton type="text" class="close" size={'large'} onClick={() => actions.close()} icon={<CloseOutlined />} />
|
<CloseButton type="text" class="close" size={'large'} onClick={() => actions.close()} icon={<CloseOutlined />} />
|
||||||
</div>
|
</div>
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
<div class="avatar">
|
<div class="avatar" onClick={() => setLogoVisible(true)}>
|
||||||
<img class="logo" src={ state.imageUrl } alt="" />
|
<Logo />
|
||||||
<div class="logoedit">
|
<div class="logoedit">
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</div>
|
</div>
|
||||||
@ -66,16 +93,13 @@ export function Profile(props) {
|
|||||||
<Button type="text" onClick={() => setInfoVisible(true)} icon={<EditOutlined />} />
|
<Button type="text" onClick={() => setInfoVisible(true)} icon={<EditOutlined />} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Modal
|
<Modal title="Profile Info" centered visible={infoVisible} okText="Save"
|
||||||
title="Profile Info"
|
onOk={() => onProfileSave()} onCancel={() => setInfoVisible(false)}>
|
||||||
centered
|
|
||||||
visible={infoVisible}
|
|
||||||
okText="Save"
|
|
||||||
onOk={() => onProfileSave()}
|
|
||||||
onCancel={() => setInfoVisible(false)}
|
|
||||||
>
|
|
||||||
<ProfileInfo state={state} actions={actions} />
|
<ProfileInfo state={state} actions={actions} />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal title="Profile Image" centered visible={logoVisible} footer={Footer}>
|
||||||
|
<ProfileImage state={state} actions={actions} />
|
||||||
|
</Modal>
|
||||||
</ProfileWrapper>
|
</ProfileWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,16 @@ export const ProfileWrapper = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const ModalFooter = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.select {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const CloseButton = styled(Button)`
|
export const CloseButton = styled(Button)`
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #aaaaaa;
|
color: #aaaaaa;
|
||||||
|
33
net/web/src/User/Profile/ProfileImage/ProfileImage.jsx
Normal file
33
net/web/src/User/Profile/ProfileImage/ProfileImage.jsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import Cropper from 'react-easy-crop'
|
||||||
|
import { UserOutlined } from '@ant-design/icons';
|
||||||
|
import { ProfileImageWrapper, ProfileDefaultImage } from './ProfileImage.styled';
|
||||||
|
|
||||||
|
export function ProfileImage({ state, actions }) {
|
||||||
|
|
||||||
|
const [crop, setCrop] = useState({ x: 0, y: 0 })
|
||||||
|
const [zoom, setZoom] = useState(1)
|
||||||
|
|
||||||
|
const onCropComplete = (croppedArea, croppedAreaPixels) => {
|
||||||
|
console.log("crop complete");
|
||||||
|
};
|
||||||
|
|
||||||
|
const Logo = () => {
|
||||||
|
if (state.modalImage == null) {
|
||||||
|
return <ProfileDefaultImage class="logo"><UserOutlined /></ProfileDefaultImage>
|
||||||
|
}
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelect = () => {
|
||||||
|
console.log("ON SELECT");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProfileImageWrapper>
|
||||||
|
<Cropper onClick={() => onSelect()} image={state.modalImage} crop={crop} zoom={zoom} aspect={1}
|
||||||
|
onCropChange={setCrop} onCropComplete={onCropComplete} onZoomChange={setZoom} />
|
||||||
|
<Logo />
|
||||||
|
</ProfileImageWrapper>
|
||||||
|
)
|
||||||
|
}
|
25
net/web/src/User/Profile/ProfileImage/ProfileImage.styled.js
Normal file
25
net/web/src/User/Profile/ProfileImage/ProfileImage.styled.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const ProfileImageWrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
height: 200px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ProfileDefaultImage = styled.div`
|
||||||
|
width: 192px;
|
||||||
|
height: 192px;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
border-radius: 8px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
position: absolute;
|
||||||
|
color: #888888;
|
||||||
|
font-size: 6em;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
@ -14,6 +14,7 @@ export function useProfile() {
|
|||||||
modalName: '',
|
modalName: '',
|
||||||
modalLocation: '',
|
modalLocation: '',
|
||||||
modalDescription: '',
|
modalDescription: '',
|
||||||
|
modalImage: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -36,6 +37,9 @@ export function useProfile() {
|
|||||||
setModalDescription: (value) => {
|
setModalDescription: (value) => {
|
||||||
updateState({ modalDescription: value });
|
updateState({ modalDescription: value });
|
||||||
},
|
},
|
||||||
|
setModalImage: (value) => {
|
||||||
|
updateState({ modalImage: value });
|
||||||
|
},
|
||||||
setModalProfile: async () => {
|
setModalProfile: async () => {
|
||||||
let set = false
|
let set = false
|
||||||
if(!state.modalBusy) {
|
if(!state.modalBusy) {
|
||||||
@ -58,8 +62,10 @@ export function useProfile() {
|
|||||||
let profile = app.state.Data.profile;
|
let profile = app.state.Data.profile;
|
||||||
if (profile.image != null) {
|
if (profile.image != null) {
|
||||||
updateState({ imageUrl: app.actions.profileImageUrl() })
|
updateState({ imageUrl: app.actions.profileImageUrl() })
|
||||||
|
updateState({ modalImage: app.actions.profileImageUrl() })
|
||||||
} else {
|
} else {
|
||||||
updateState({ imageUrl: '' })
|
updateState({ imageUrl: '' })
|
||||||
|
updateState({ modalImage: null })
|
||||||
}
|
}
|
||||||
updateState({ name: profile.name });
|
updateState({ name: profile.name });
|
||||||
updateState({ modalName: profile.name });
|
updateState({ modalName: profile.name });
|
||||||
|
@ -6187,6 +6187,11 @@ normalize-url@^6.0.1:
|
|||||||
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz"
|
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz"
|
||||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||||
|
|
||||||
|
normalize-wheel@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
|
||||||
|
integrity sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU=
|
||||||
|
|
||||||
npm-run-path@^4.0.1:
|
npm-run-path@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz"
|
resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz"
|
||||||
@ -7630,6 +7635,14 @@ react-dom@^17.0.2:
|
|||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
scheduler "^0.20.2"
|
scheduler "^0.20.2"
|
||||||
|
|
||||||
|
react-easy-crop@^4.1.4:
|
||||||
|
version "4.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-easy-crop/-/react-easy-crop-4.1.4.tgz#71543f7156f038b37bc45c4b7eb141173e623d38"
|
||||||
|
integrity sha512-Gw07jxdeJAGQAFybyNa2HYzviNjNJ3lyNTEApbA0zHcZ9N8f77iH3w1dEB5PbxbrG+LXvxFmMPNgMBoDpz27tQ==
|
||||||
|
dependencies:
|
||||||
|
normalize-wheel "^1.0.1"
|
||||||
|
tslib "2.0.1"
|
||||||
|
|
||||||
react-error-overlay@^6.0.10:
|
react-error-overlay@^6.0.10:
|
||||||
version "6.0.10"
|
version "6.0.10"
|
||||||
resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz"
|
resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz"
|
||||||
@ -8738,6 +8751,11 @@ tsconfig-paths@^3.12.0:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-bom "^3.0.0"
|
strip-bom "^3.0.0"
|
||||||
|
|
||||||
|
tslib@2.0.1:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
|
||||||
|
integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
|
||||||
|
|
||||||
tslib@^1.8.1:
|
tslib@^1.8.1:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
|
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
|
||||||
|
Loading…
Reference in New Issue
Block a user