kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Refactor dropdown into 'addon' on Input
rodzic
99c67916dd
commit
07f6935789
|
@ -20,7 +20,7 @@ interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxL
|
||||||
className?: string,
|
className?: string,
|
||||||
/** Extra class names for the outer <div> element. */
|
/** Extra class names for the outer <div> element. */
|
||||||
outerClassName?: string,
|
outerClassName?: string,
|
||||||
/** URL to the svg icon. */
|
/** URL to the svg icon. Cannot be used with addon. */
|
||||||
icon?: string,
|
icon?: string,
|
||||||
/** Internal input name. */
|
/** Internal input name. */
|
||||||
name?: string,
|
name?: string,
|
||||||
|
@ -31,9 +31,11 @@ interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxL
|
||||||
/** Change event handler for the input. */
|
/** Change event handler for the input. */
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
||||||
/** HTML input type. */
|
/** HTML input type. */
|
||||||
type: 'text' | 'number' | 'email' | 'tel' | 'password',
|
type?: 'text' | 'number' | 'email' | 'tel' | 'password',
|
||||||
/** Whether to display the input in red. */
|
/** Whether to display the input in red. */
|
||||||
hasError?: boolean,
|
hasError?: boolean,
|
||||||
|
/** An element to display as prefix to input. Cannot be used with icon. */
|
||||||
|
addon?: React.ReactElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Form input element. */
|
/** Form input element. */
|
||||||
|
@ -41,7 +43,7 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
|
||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const { type = 'text', icon, className, outerClassName, hasError, ...filteredProps } = props;
|
const { type = 'text', icon, className, outerClassName, hasError, addon, ...filteredProps } = props;
|
||||||
|
|
||||||
const [revealed, setRevealed] = React.useState(false);
|
const [revealed, setRevealed] = React.useState(false);
|
||||||
|
|
||||||
|
@ -52,13 +54,19 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('mt-1 relative shadow-sm', outerClassName)}>
|
<div className={classNames('mt-1 relative rounded-md shadow-sm', outerClassName)}>
|
||||||
{icon ? (
|
{icon ? (
|
||||||
<div className='absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none'>
|
<div className='absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none'>
|
||||||
<Icon src={icon} className='h-4 w-4 text-gray-400' aria-hidden='true' />
|
<Icon src={icon} className='h-4 w-4 text-gray-400' aria-hidden='true' />
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
{addon ? (
|
||||||
|
<div className='absolute inset-y-0 left-0 flex items-center'>
|
||||||
|
{addon}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<input
|
<input
|
||||||
{...filteredProps}
|
{...filteredProps}
|
||||||
type={revealed ? 'text' : type}
|
type={revealed ? 'text' : type}
|
||||||
|
@ -69,6 +77,7 @@ const Input = React.forwardRef<HTMLInputElement, IInput>(
|
||||||
'pr-7': isPassword,
|
'pr-7': isPassword,
|
||||||
'text-red-600 border-red-600': hasError,
|
'text-red-600 border-red-600': hasError,
|
||||||
'pl-8': typeof icon !== 'undefined',
|
'pl-8': typeof icon !== 'undefined',
|
||||||
|
'pl-16': typeof addon !== 'undefined',
|
||||||
}, className)}
|
}, className)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { Icon, HStack } from 'soapbox/components/ui';
|
|
||||||
import DropdownMenu from 'soapbox/containers/dropdown_menu_container';
|
|
||||||
import { COUNTRY_CODES, CountryCode } from 'soapbox/utils/phone';
|
import { COUNTRY_CODES, CountryCode } from 'soapbox/utils/phone';
|
||||||
|
|
||||||
import type { Menu } from 'soapbox/components/dropdown_menu';
|
|
||||||
|
|
||||||
interface ICountryCodeDropdown {
|
interface ICountryCodeDropdown {
|
||||||
countryCode: CountryCode,
|
countryCode: CountryCode,
|
||||||
onChange(countryCode: CountryCode): void,
|
onChange(countryCode: CountryCode): void,
|
||||||
|
@ -13,27 +9,16 @@ interface ICountryCodeDropdown {
|
||||||
|
|
||||||
/** Dropdown menu to select a country code. */
|
/** Dropdown menu to select a country code. */
|
||||||
const CountryCodeDropdown: React.FC<ICountryCodeDropdown> = ({ countryCode, onChange }) => {
|
const CountryCodeDropdown: React.FC<ICountryCodeDropdown> = ({ countryCode, onChange }) => {
|
||||||
|
|
||||||
const handleMenuItem = (code: CountryCode) => {
|
|
||||||
return () => {
|
|
||||||
onChange(code);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const menu: Menu = COUNTRY_CODES.map(code => ({
|
|
||||||
text: <>+{code}</>,
|
|
||||||
action: handleMenuItem(code),
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu items={menu}>
|
<select
|
||||||
<button type='button' className='px-4 items-center'>
|
value={countryCode}
|
||||||
<HStack space={1} alignItems='center'>
|
className='h-full py-0 pl-3 pr-7 text-base bg-transparent border-transparent focus:outline-none focus:ring-primary-500 dark:text-white sm:text-sm rounded-md'
|
||||||
<div>+{countryCode}</div>
|
onChange={(event) => onChange(event.target.value as any)}
|
||||||
<Icon className='w-4 h-4 stroke-primary-600' src={require('@tabler/icons/chevron-down.svg')} />
|
>
|
||||||
</HStack>
|
{COUNTRY_CODES.map((code) => (
|
||||||
</button>
|
<option value={code} key={code}>+{code}</option>
|
||||||
</DropdownMenu>
|
))}
|
||||||
|
</select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
import { CountryCode } from 'soapbox/utils/phone';
|
import { CountryCode } from 'soapbox/utils/phone';
|
||||||
|
|
||||||
import HStack from '../hstack/hstack';
|
|
||||||
import Input from '../input/input';
|
import Input from '../input/input';
|
||||||
|
|
||||||
import CountryCodeDropdown from './country-code-dropdown';
|
import CountryCodeDropdown from './country-code-dropdown';
|
||||||
|
@ -61,23 +60,17 @@ const PhoneInput: React.FC<IPhoneInput> = (props) => {
|
||||||
}, [countryCode, nationalNumber]);
|
}, [countryCode, nationalNumber]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack className='mt-1 shadow-sm'>
|
<Input
|
||||||
<div className='dark:bg-slate-800 border border-solid border-r-0 border-gray-300 dark:border-gray-600 flex items-center rounded-l-md'>
|
onChange={handleChange}
|
||||||
|
value={nationalNumber}
|
||||||
|
addon={
|
||||||
<CountryCodeDropdown
|
<CountryCodeDropdown
|
||||||
countryCode={countryCode}
|
countryCode={countryCode}
|
||||||
onChange={setCountryCode}
|
onChange={setCountryCode}
|
||||||
/>
|
/>
|
||||||
</div>
|
}
|
||||||
|
{...rest}
|
||||||
<Input
|
/>
|
||||||
type='text'
|
|
||||||
outerClassName='mt-0 shadow-none'
|
|
||||||
className='rounded-l-none'
|
|
||||||
onChange={handleChange}
|
|
||||||
value={nationalNumber}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue