diff --git a/package-lock.json b/package-lock.json index c2e31f6..d06472b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,13 @@ "name": "exercise-password-generator", "version": "0.1.0", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.36", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@fortawesome/react-fontawesome": "^0.1.17", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "copy-to-clipboard": "^3.3.1", "eslint-config-airbnb": "^18.0.1", "eslint-plugin-jsx-a11y": "^6.2.3", "react": "^17.0.2", @@ -1939,6 +1943,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz", + "integrity": "sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || >=1.3.0-beta1", + "react": ">=16.x" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -5373,6 +5422,14 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "node_modules/copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, "node_modules/core-js": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.0.tgz", @@ -16472,6 +16529,11 @@ "node": ">=8.0" } }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -19025,6 +19087,35 @@ } } }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz", + "integrity": "sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==", + "requires": { + "prop-types": "^15.8.1" + } + }, "@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -21580,6 +21671,14 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, + "copy-to-clipboard": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz", + "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==", + "requires": { + "toggle-selection": "^1.0.6" + } + }, "core-js": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.0.tgz", @@ -29534,6 +29633,11 @@ "is-number": "^7.0.0" } }, + "toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" + }, "toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", diff --git a/package.json b/package.json index 7397ef0..a00b416 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,13 @@ "node": "16" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.36", + "@fortawesome/free-solid-svg-icons": "^5.15.4", + "@fortawesome/react-fontawesome": "^0.1.17", "@testing-library/jest-dom": "^5.16.1", "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", + "copy-to-clipboard": "^3.3.1", "eslint-config-airbnb": "^18.0.1", "eslint-plugin-jsx-a11y": "^6.2.3", "react": "^17.0.2", diff --git a/public/index.html b/public/index.html index 0b43080..a078984 100644 --- a/public/index.html +++ b/public/index.html @@ -24,6 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> + React App diff --git a/src/components/Main/Main.css b/src/components/Main/Main.css index 29845cd..3c7701b 100644 --- a/src/components/Main/Main.css +++ b/src/components/Main/Main.css @@ -5,6 +5,7 @@ height: 85%; justify-content: space-between; margin: auto; + position: relative; } .main_password { @@ -13,3 +14,16 @@ background-color: #1E223F; padding: 20px; } + +.Copied, .Copy { + border: 1px solid #1E223F; + border-radius: 10px; + color: #fafafa; + position: absolute; + right: 0; + font-size: 0.7em; + padding: 5px; + width: 50px; + top: 0; + background-color: #1E223F; +} diff --git a/src/components/Main/Main.js b/src/components/Main/Main.js index 1cd0ffa..9dda5bd 100644 --- a/src/components/Main/Main.js +++ b/src/components/Main/Main.js @@ -1,19 +1,38 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; +import copy from 'copy-to-clipboard'; +import { copiedPassword } from '../../redux/actions'; import LengthCaracters from '../LengthCaracters/LengthCaracters'; import Settings from '../Settings/Settings'; import './Main.css' class Main extends Component { + copyPassword = () => { + const { password, copied } = this.props; + copy(password); + copied(); + } + render() { - const { password } = this.props; + const { password, btnCopy } = this.props; return (
{ password.length === 0 ?

CLICK GENERATE

- :

{ password }

+ : ( + <> + +

{ password }

+ + ) } @@ -25,11 +44,18 @@ class Main extends Component { Main.propTypes = { password: PropTypes.string, + btnCopy: PropTypes.string, + copied: PropTypes.func, }.isRequired; const mapStateToProps = (state) => { const { password } = state.passwordReducer; - return { password }; + const { btnCopy } = state.copyPasswordReducer; + return { password, btnCopy }; } -export default connect(mapStateToProps, null)(Main); +const mapDispatchToProps = (dispatch) => ({ + copied: () => dispatch(copiedPassword()), +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Main); diff --git a/src/components/Settings/Settings.js b/src/components/Settings/Settings.js index cc07c2f..09fdfbb 100644 --- a/src/components/Settings/Settings.js +++ b/src/components/Settings/Settings.js @@ -4,7 +4,7 @@ import { connect } from 'react-redux'; import Checkbox from '../Checkbox/Checkbox'; import BtnGenerate from '../BtnGenerate/BtnGenerate'; import './Settings.css'; -import { savePassword } from '../../redux/actions'; +import { savePassword, copyPassword } from '../../redux/actions'; import caracters from '../../data/caracters'; class Settings extends Component { @@ -42,10 +42,11 @@ class Settings extends Component { handleClick = (event) => { event.preventDefault(); - const { savePassword } = this.props; + const { savePassword, copyPassword } = this.props; const password = this.createPassword(); savePassword(password); + copyPassword(); } handleChangeCheckbox = ({ target }) => { @@ -122,7 +123,8 @@ const mapStateToProps = (state) => { } const mapDispatchToProps = (dispatch) => ({ - savePassword: (password) => dispatch(savePassword(password)) + savePassword: (password) => dispatch(savePassword(password)), + copyPassword: () => dispatch(copyPassword()), }) export default connect(mapStateToProps, mapDispatchToProps)(Settings); diff --git a/src/redux/actions/index.js b/src/redux/actions/index.js index 85169d2..331bd23 100644 --- a/src/redux/actions/index.js +++ b/src/redux/actions/index.js @@ -1,5 +1,7 @@ export const LENGTH_PASSWORD = 'LENGTH_PASSWORD'; export const PASSWORD = 'PASSWORD'; +export const COPY_PASSWORD = 'COPY_PASSWORD'; +export const COPIED_PASSWORD = 'COPIED_PASSWORD'; export const lengthPassword = (value) => ({ type: LENGTH_PASSWORD, @@ -10,3 +12,13 @@ export const savePassword = (value) => ({ type: PASSWORD, value, }); + +export const copyPassword = (value) => ({ + type: COPY_PASSWORD, + value, +}); + +export const copiedPassword = (value) => ({ + type: COPIED_PASSWORD, + value, +}); diff --git a/src/redux/reducers/CopyPasswordReducer.js b/src/redux/reducers/CopyPasswordReducer.js new file mode 100644 index 0000000..0b5f295 --- /dev/null +++ b/src/redux/reducers/CopyPasswordReducer.js @@ -0,0 +1,24 @@ +import { COPY_PASSWORD, COPIED_PASSWORD } from '../actions'; + +const INITIAL_STATE = { + btnCopy: 'Copy', +}; + +const copyPasswordReducer = (state = INITIAL_STATE, action) => { + switch (action.type) { + case COPY_PASSWORD: + return { + ...state, + btnCopy: 'Copy', + }; + case COPIED_PASSWORD: + return { + ...state, + btnCopy: 'Copied', + }; + default: + return state; + } +}; + +export default copyPasswordReducer; diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js index a91a6dd..1409954 100644 --- a/src/redux/reducers/index.js +++ b/src/redux/reducers/index.js @@ -1,10 +1,12 @@ import { combineReducers } from 'redux'; import lengthPasswordReducer from './LengthPasswordReducer'; import passwordReducer from './passwordReducer'; +import copyPasswordReducer from './CopyPasswordReducer'; const rootReducer = combineReducers({ lengthPasswordReducer, passwordReducer, + copyPasswordReducer, }); export default rootReducer;