mirror of
https://github.com/plankanban/planka.git
synced 2025-07-19 05:09:43 +02:00
Add visibility toggle of password input. Closes #2
This commit is contained in:
parent
7a3805e64c
commit
390d96cc19
25 changed files with 111 additions and 92 deletions
24
client/package-lock.json
generated
24
client/package-lock.json
generated
|
@ -6161,9 +6161,9 @@
|
||||||
"integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ=="
|
"integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ=="
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.5.1",
|
"version": "4.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.2.tgz",
|
||||||
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
|
"integrity": "sha512-29Zxv/cynYB7mkT1rVWQnV7mGX6v7H/miQ6dbEpYTKq5eJBN7PsRB+ViYJlcT6JINTSu4dVB9kOqEun78h6Exg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
|
@ -10728,9 +10728,9 @@
|
||||||
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
|
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "1.18.2",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
||||||
"integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==",
|
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier-eslint": {
|
"prettier-eslint": {
|
||||||
|
@ -10788,9 +10788,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "3.3.6",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.1.tgz",
|
||||||
"integrity": "sha512-u4oM8SHwmDuh5mWZdDg9UwNVq5s1uqq6ZDLLIs07VY+VJU91i3h4f3K/pgFvtUQPGdeStrZ+odKyfyt4EnKHfA==",
|
"integrity": "sha512-KX/dnuY/J8FtEwbnrzmAjUYgLqtk+cxM86hfG60LGiW3MmltIc2yAmDgBgEkfm0blZhUrdr1Zd84J2Y14mLxzg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint-scope": {
|
"eslint-scope": {
|
||||||
|
@ -13730,9 +13730,9 @@
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.6.4",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz",
|
||||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript-compare": {
|
"typescript-compare": {
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||||
"eslint-plugin-react": "^7.16.0",
|
"eslint-plugin-react": "^7.16.0",
|
||||||
"eslint-plugin-react-hooks": "^1.7.0",
|
"eslint-plugin-react-hooks": "^1.7.0",
|
||||||
|
"prettier-eslint": "^9.0.0",
|
||||||
"prettier-eslint-cli": "^5.0.0"
|
"prettier-eslint-cli": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,11 @@ import React, {
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form, Message } from 'semantic-ui-react';
|
import { Button, Form, Message } from 'semantic-ui-react';
|
||||||
|
import { usePrevious } from '../../lib/hooks';
|
||||||
import { withPopup } from '../../lib/popup';
|
import { withPopup } from '../../lib/popup';
|
||||||
import { Input, Popup } from '../../lib/custom-ui';
|
import { Input, Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import { useForm, usePrevious } from '../../hooks';
|
import { useForm } from '../../hooks';
|
||||||
|
|
||||||
import styles from './AddUserPopup.module.css';
|
import styles from './AddUserPopup.module.css';
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ const AddUserPopup = React.memo(
|
||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
<div className={styles.text}>{t('common.password')}</div>
|
<div className={styles.text}>{t('common.password')}</div>
|
||||||
<Input
|
<Input.Password
|
||||||
fluid
|
fluid
|
||||||
ref={passwordField}
|
ref={passwordField}
|
||||||
name="password"
|
name="password"
|
||||||
|
|
|
@ -4,10 +4,9 @@ import React, {
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form, Input } from 'semantic-ui-react';
|
import { Button, Form, Input } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, useToggle } from '../../lib/hooks';
|
||||||
|
|
||||||
import {
|
import { useClosableForm, useForm } from '../../hooks';
|
||||||
useClosableForm, useDidUpdate, useForm, useToggle,
|
|
||||||
} from '../../hooks';
|
|
||||||
|
|
||||||
import styles from './AddList.module.css';
|
import styles from './AddList.module.css';
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@ import React, { useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Progress } from 'semantic-ui-react';
|
import { Progress } from 'semantic-ui-react';
|
||||||
|
import { useToggle } from '../../lib/hooks';
|
||||||
import { useToggle } from '../../hooks';
|
|
||||||
|
|
||||||
import styles from './Tasks.module.css';
|
import styles from './Tasks.module.css';
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,9 @@ import React, { useCallback, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
import { TextArea } from 'semantic-ui-react';
|
import { TextArea } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, usePrevious } from '../../lib/hooks';
|
||||||
|
|
||||||
import { useDidUpdate, useField, usePrevious } from '../../hooks';
|
import { useField } from '../../hooks';
|
||||||
|
|
||||||
import styles from './NameField.module.css';
|
import styles from './NameField.module.css';
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, useToggle } from '../../../lib/hooks';
|
||||||
|
|
||||||
import {
|
import { useClosableForm, useForm } from '../../../hooks';
|
||||||
useClosableForm, useDidUpdate, useForm, useToggle,
|
|
||||||
} from '../../../hooks';
|
|
||||||
|
|
||||||
import styles from './Add.module.css';
|
import styles from './Add.module.css';
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@ import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import DatePicker from 'react-datepicker';
|
import DatePicker from 'react-datepicker';
|
||||||
import { Button, Form } from 'semantic-ui-react';
|
import { Button, Form } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, useToggle } from '../../lib/hooks';
|
||||||
import { Input, Popup } from '../../lib/custom-ui';
|
import { Input, Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import { useDidUpdate, useForm, useToggle } from '../../hooks';
|
import { useForm } from '../../hooks';
|
||||||
|
|
||||||
import styles from './EditDueDateStep.module.css';
|
import styles from './EditDueDateStep.module.css';
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form } from 'semantic-ui-react';
|
import { Button, Form } from 'semantic-ui-react';
|
||||||
|
import { useToggle } from '../../lib/hooks';
|
||||||
import { Input, Popup } from '../../lib/custom-ui';
|
import { Input, Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import { useForm, useToggle } from '../../hooks';
|
import { useForm } from '../../hooks';
|
||||||
import {
|
import {
|
||||||
createTimer, getTimerParts, startTimer, stopTimer, updateTimer,
|
createTimer, getTimerParts, startTimer, stopTimer, updateTimer,
|
||||||
} from '../../utils/timer';
|
} from '../../utils/timer';
|
||||||
|
@ -114,7 +115,7 @@ const EditTimerStep = React.memo(({
|
||||||
<div className={styles.fieldWrapper}>
|
<div className={styles.fieldWrapper}>
|
||||||
<div className={styles.fieldBox}>
|
<div className={styles.fieldBox}>
|
||||||
<div className={styles.text}>{t('common.hours')}</div>
|
<div className={styles.text}>{t('common.hours')}</div>
|
||||||
<Input
|
<Input.Mask
|
||||||
ref={hoursField}
|
ref={hoursField}
|
||||||
name="hours"
|
name="hours"
|
||||||
value={data.hours}
|
value={data.hours}
|
||||||
|
@ -126,7 +127,7 @@ const EditTimerStep = React.memo(({
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.fieldBox}>
|
<div className={styles.fieldBox}>
|
||||||
<div className={styles.text}>{t('common.minutes')}</div>
|
<div className={styles.text}>{t('common.minutes')}</div>
|
||||||
<Input
|
<Input.Mask
|
||||||
ref={minutesField}
|
ref={minutesField}
|
||||||
name="minutes"
|
name="minutes"
|
||||||
value={data.minutes}
|
value={data.minutes}
|
||||||
|
@ -138,7 +139,7 @@ const EditTimerStep = React.memo(({
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.fieldBox}>
|
<div className={styles.fieldBox}>
|
||||||
<div className={styles.text}>{t('common.seconds')}</div>
|
<div className={styles.text}>{t('common.seconds')}</div>
|
||||||
<Input
|
<Input.Mask
|
||||||
ref={secondsField}
|
ref={secondsField}
|
||||||
name="seconds"
|
name="seconds"
|
||||||
value={data.seconds}
|
value={data.seconds}
|
||||||
|
|
|
@ -5,10 +5,9 @@ import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import TextareaAutosize from 'react-textarea-autosize';
|
import TextareaAutosize from 'react-textarea-autosize';
|
||||||
import { Button, Form, TextArea } from 'semantic-ui-react';
|
import { Button, Form, TextArea } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, useToggle } from '../../lib/hooks';
|
||||||
|
|
||||||
import {
|
import { useClosableForm, useForm } from '../../hooks';
|
||||||
useClosableForm, useDidUpdate, useForm, useToggle,
|
|
||||||
} from '../../hooks';
|
|
||||||
|
|
||||||
import styles from './AddCard.module.css';
|
import styles from './AddCard.module.css';
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,10 @@ import isEmail from 'validator/lib/isEmail';
|
||||||
import {
|
import {
|
||||||
Form, Grid, Header, Message,
|
Form, Grid, Header, Message,
|
||||||
} from 'semantic-ui-react';
|
} from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, usePrevious, useToggle } from '../../lib/hooks';
|
||||||
import { Input } from '../../lib/custom-ui';
|
import { Input } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import {
|
import { useForm } from '../../hooks';
|
||||||
useDidUpdate, useForm, usePrevious, useToggle,
|
|
||||||
} from '../../hooks';
|
|
||||||
|
|
||||||
import styles from './Login.module.css';
|
import styles from './Login.module.css';
|
||||||
|
|
||||||
|
@ -156,10 +155,9 @@ const Login = React.memo(
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.inputWrapper}>
|
<div className={styles.inputWrapper}>
|
||||||
<div className={styles.inputLabel}>{t('common.password')}</div>
|
<div className={styles.inputLabel}>{t('common.password')}</div>
|
||||||
<Input
|
<Input.Password
|
||||||
fluid
|
fluid
|
||||||
ref={passwordField}
|
ref={passwordField}
|
||||||
type="password"
|
|
||||||
name="password"
|
name="password"
|
||||||
value={data.password}
|
value={data.password}
|
||||||
readOnly={isSubmitting}
|
readOnly={isSubmitting}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
import React, { useCallback, useEffect, useRef } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useForceUpdate, usePrevious } from '../../lib/hooks';
|
||||||
|
|
||||||
import { useForceUpdate, usePrevious } from '../../hooks';
|
|
||||||
import { formatTimer } from '../../utils/timer';
|
import { formatTimer } from '../../utils/timer';
|
||||||
|
|
||||||
import styles from './Timer.module.css';
|
import styles from './Timer.module.css';
|
||||||
|
|
|
@ -5,11 +5,10 @@ import React, {
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form, Message } from 'semantic-ui-react';
|
import { Button, Form, Message } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, usePrevious, useToggle } from '../../lib/hooks';
|
||||||
import { Input, Popup } from '../../lib/custom-ui';
|
import { Input, Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import {
|
import { useForm } from '../../hooks';
|
||||||
useDidUpdate, useForm, usePrevious, useToggle,
|
|
||||||
} from '../../hooks';
|
|
||||||
|
|
||||||
import styles from './EditNameStep.module.css';
|
import styles from './EditNameStep.module.css';
|
||||||
|
|
||||||
|
@ -145,10 +144,9 @@ const EditEmailStep = React.memo(
|
||||||
{data.email.trim() !== email && (
|
{data.email.trim() !== email && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.text}>{t('common.currentPassword')}</div>
|
<div className={styles.text}>{t('common.currentPassword')}</div>
|
||||||
<Input
|
<Input.Password
|
||||||
fluid
|
fluid
|
||||||
ref={currentPasswordField}
|
ref={currentPasswordField}
|
||||||
type="password"
|
|
||||||
name="currentPassword"
|
name="currentPassword"
|
||||||
value={data.currentPassword}
|
value={data.currentPassword}
|
||||||
className={styles.field}
|
className={styles.field}
|
||||||
|
|
|
@ -4,11 +4,10 @@ import React, {
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button, Form, Message } from 'semantic-ui-react';
|
import { Button, Form, Message } from 'semantic-ui-react';
|
||||||
|
import { useDidUpdate, usePrevious, useToggle } from '../../lib/hooks';
|
||||||
import { Input, Popup } from '../../lib/custom-ui';
|
import { Input, Popup } from '../../lib/custom-ui';
|
||||||
|
|
||||||
import {
|
import { useForm } from '../../hooks';
|
||||||
useDidUpdate, useForm, usePrevious, useToggle,
|
|
||||||
} from '../../hooks';
|
|
||||||
|
|
||||||
import styles from './EditNameStep.module.css';
|
import styles from './EditNameStep.module.css';
|
||||||
|
|
||||||
|
@ -107,7 +106,7 @@ const EditPasswordStep = React.memo(
|
||||||
)}
|
)}
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
<div className={styles.text}>{t('common.newPassword')}</div>
|
<div className={styles.text}>{t('common.newPassword')}</div>
|
||||||
<Input
|
<Input.Password
|
||||||
fluid
|
fluid
|
||||||
ref={passwordField}
|
ref={passwordField}
|
||||||
name="password"
|
name="password"
|
||||||
|
@ -116,10 +115,9 @@ const EditPasswordStep = React.memo(
|
||||||
onChange={handleFieldChange}
|
onChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
<div className={styles.text}>{t('common.currentPassword')}</div>
|
<div className={styles.text}>{t('common.currentPassword')}</div>
|
||||||
<Input
|
<Input.Password
|
||||||
fluid
|
fluid
|
||||||
ref={currentPasswordField}
|
ref={currentPasswordField}
|
||||||
type="password"
|
|
||||||
name="currentPassword"
|
name="currentPassword"
|
||||||
value={data.currentPassword}
|
value={data.currentPassword}
|
||||||
className={styles.field}
|
className={styles.field}
|
||||||
|
|
|
@ -1,19 +1,8 @@
|
||||||
import usePrevious from './use-previous';
|
|
||||||
import useField from './use-field';
|
import useField from './use-field';
|
||||||
import useForm from './use-form';
|
import useForm from './use-form';
|
||||||
import useSteps from './use-steps';
|
import useSteps from './use-steps';
|
||||||
import useToggle from './use-toggle';
|
|
||||||
import useForceUpdate from './use-force-update';
|
|
||||||
import useClosableForm from './use-closable-form';
|
import useClosableForm from './use-closable-form';
|
||||||
import useDidUpdate from './use-did-update';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
usePrevious,
|
useField, useForm, useSteps, useClosableForm,
|
||||||
useField,
|
|
||||||
useForm,
|
|
||||||
useSteps,
|
|
||||||
useToggle,
|
|
||||||
useForceUpdate,
|
|
||||||
useClosableForm,
|
|
||||||
useDidUpdate,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,28 +1,11 @@
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { Input as SemanticUIInput } from 'semantic-ui-react';
|
import { Input as SemanticUIInput } from 'semantic-ui-react';
|
||||||
|
|
||||||
import MaskedInput from './MaskedInput';
|
import InputPassword from './InputPassword';
|
||||||
|
import InputMask from './InputMask';
|
||||||
|
|
||||||
const Input = React.forwardRef(({ mask, maskChar, ...props }, ref) => {
|
const Input = SemanticUIInput;
|
||||||
const nextProps = props;
|
|
||||||
|
|
||||||
if (mask) {
|
Input.Password = InputPassword;
|
||||||
nextProps.input = <MaskedInput mask={mask} maskChar={maskChar} />;
|
Input.Mask = InputMask;
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
export default Input;
|
||||||
return <SemanticUIInput {...nextProps} ref={ref} />;
|
|
||||||
});
|
|
||||||
|
|
||||||
Input.propTypes = {
|
|
||||||
mask: PropTypes.string,
|
|
||||||
maskChar: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
Input.defaultProps = {
|
|
||||||
mask: undefined,
|
|
||||||
maskChar: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default React.memo(Input);
|
|
||||||
|
|
21
client/src/lib/custom-ui/components/Input/InputMask.jsx
Normal file
21
client/src/lib/custom-ui/components/Input/InputMask.jsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Input } from 'semantic-ui-react';
|
||||||
|
|
||||||
|
import MaskedInput from './MaskedInput';
|
||||||
|
|
||||||
|
const InputMask = React.forwardRef(({ mask, maskChar, ...props }, ref) => (
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
<Input {...props} ref={ref} input={<MaskedInput mask={mask} maskChar={maskChar} />} />
|
||||||
|
));
|
||||||
|
|
||||||
|
InputMask.propTypes = {
|
||||||
|
mask: PropTypes.string.isRequired,
|
||||||
|
maskChar: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputMask.defaultProps = {
|
||||||
|
maskChar: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(InputMask);
|
22
client/src/lib/custom-ui/components/Input/InputPassword.jsx
Normal file
22
client/src/lib/custom-ui/components/Input/InputPassword.jsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { Icon, Input } from 'semantic-ui-react';
|
||||||
|
import { useToggle } from '../../../hooks';
|
||||||
|
|
||||||
|
const InputPassword = React.forwardRef((props, ref) => {
|
||||||
|
const [isHidden, toggleHidden] = useToggle(true);
|
||||||
|
|
||||||
|
const handleToggleClick = useCallback(() => {
|
||||||
|
toggleHidden();
|
||||||
|
}, [toggleHidden]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
{...props} // eslint-disable-line react/jsx-props-no-spreading
|
||||||
|
ref={ref}
|
||||||
|
type={isHidden ? 'password' : 'text'}
|
||||||
|
icon={<Icon link name={isHidden ? 'eye slash' : 'eye'} onClick={handleToggleClick} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default React.memo(InputPassword);
|
8
client/src/lib/hooks/index.js
Normal file
8
client/src/lib/hooks/index.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import usePrevious from './use-previous';
|
||||||
|
import useToggle from './use-toggle';
|
||||||
|
import useForceUpdate from './use-force-update';
|
||||||
|
import useDidUpdate from './use-did-update';
|
||||||
|
|
||||||
|
export {
|
||||||
|
usePrevious, useToggle, useForceUpdate, useDidUpdate,
|
||||||
|
};
|
18
server/package-lock.json
generated
18
server/package-lock.json
generated
|
@ -5367,9 +5367,9 @@
|
||||||
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
|
"integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"version": "1.18.2",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
|
||||||
"integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==",
|
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prettier-eslint": {
|
"prettier-eslint": {
|
||||||
|
@ -5394,9 +5394,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "3.3.6",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.1.tgz",
|
||||||
"integrity": "sha512-u4oM8SHwmDuh5mWZdDg9UwNVq5s1uqq6ZDLLIs07VY+VJU91i3h4f3K/pgFvtUQPGdeStrZ+odKyfyt4EnKHfA==",
|
"integrity": "sha512-KX/dnuY/J8FtEwbnrzmAjUYgLqtk+cxM86hfG60LGiW3MmltIc2yAmDgBgEkfm0blZhUrdr1Zd84J2Y14mLxzg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7407,9 +7407,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.6.4",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz",
|
||||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uid-safe": {
|
"uid-safe": {
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
"eslint-config-airbnb-base": "^14.0.0",
|
"eslint-config-airbnb-base": "^14.0.0",
|
||||||
"eslint-plugin-import": "^2.18.2",
|
"eslint-plugin-import": "^2.18.2",
|
||||||
"nodemon": "^1.19.4",
|
"nodemon": "^1.19.4",
|
||||||
|
"prettier-eslint": "^9.0.0",
|
||||||
"prettier-eslint-cli": "^5.0.0"
|
"prettier-eslint-cli": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue