diff --git a/src/fields/Phone/PhoneInput.tsx b/src/fields/Phone/PhoneInput.tsx new file mode 100644 index 00000000..676ec002 --- /dev/null +++ b/src/fields/Phone/PhoneInput.tsx @@ -0,0 +1,43 @@ +import { Field } from '@elements/Field' +import { Label } from '@elements/Label' +import { inputStyle } from '@fields/shared/StyledInputBox' +import { forwardRef, useId, type ComponentProps } from 'react' +import { IMaskInput } from 'react-imask' +import styled from 'styled-components' + +import type { Promisable } from 'type-fest' + +export type PhoneInputProps = { + error?: string + label: string + onChange: (nextValue: string | undefined) => Promisable + value: string | undefined +} & Omit, 'onChange' | 'value'> + +export const PhoneInput = forwardRef(({ label, onChange, value, ...props }, ref) => { + const id = useId() + + return ( + + + { + onChange(nextValue) + }} + unmask + value={value} + {...props} + /> + + ) +}) + +const StyledIMaskInput = styled(IMaskInput)` + ${inputStyle} +` diff --git a/src/fields/shared/StyledInputBox.ts b/src/fields/shared/StyledInputBox.ts index e57ba854..97301c75 100644 --- a/src/fields/shared/StyledInputBox.ts +++ b/src/fields/shared/StyledInputBox.ts @@ -1,4 +1,4 @@ -import styled from 'styled-components' +import styled, { css } from 'styled-components' import { getFieldBackgroundColorFactory, @@ -25,6 +25,43 @@ const PADDING_WITH_ICON: Record = { [Size.NORMAL]: '3px 38px 6px 8px', [Size.SMALL]: '3px 38px 6px 8px' } +export const inputStyle = css` + background-color: ${getFieldBackgroundColorFactory()}; + border: solid 1px ${getFieldBorderColorFactoryForState('default')}; + border-radius: 0; + color: ${p => p.theme.color.gunMetal}; + ${p => p.$isReadOnly && `cursor: default;`} + font-size: 13px; + font-weight: 500; + line-height: 19px; + padding: 3px 8px 6px; + vertical-align: center; + width: 100%; + + &::placeholder { + color: ${getFieldPlaceholderColorFactoryForState('default')}; + } + + &:hover { + background-color: ${getFieldBackgroundColorFactory()}; + border: solid 1px ${getFieldBorderColorFactoryForState('hover')} !important; + + &::placeholder { + color: ${getFieldPlaceholderColorFactoryForState('hover')}; + } + } + + &:active, + &:focus { + background-color: ${getFieldBackgroundColorFactory()}; + border: solid 1px ${getFieldBorderColorFactoryForState('focus')} !important; + outline: 0; + + &::placeholder { + color: ${getFieldPlaceholderColorFactoryForState('focus')}; + } + } +` export const StyledInputBox = styled.div` display: flex; @@ -38,41 +75,8 @@ export const StyledInputBox = styled.div` > input, > .rs-auto-complete > input { - background-color: ${getFieldBackgroundColorFactory()}; - border: solid 1px ${getFieldBorderColorFactoryForState('default')}; - border-radius: 0; - color: ${p => p.theme.color.gunMetal}; - ${p => p.$isReadOnly && `cursor: default;`} - flex-grow: 1; - font-size: 13px; - font-weight: 500; - line-height: 1; padding: ${p => (p.$hasIcon ? PADDING_WITH_ICON[p.$size] : PADDING[p.$size])}; - vertical-align: center; - - &::placeholder { - color: ${getFieldPlaceholderColorFactoryForState('default')}; - } - - &:hover { - background-color: ${getFieldBackgroundColorFactory()}; - border: solid 1px ${getFieldBorderColorFactoryForState('hover')} !important; - - &::placeholder { - color: ${getFieldPlaceholderColorFactoryForState('hover')}; - } - } - - &:active, - &:focus { - background-color: ${getFieldBackgroundColorFactory()}; - border: solid 1px ${getFieldBorderColorFactoryForState('focus')} !important; - outline: 0; - - &::placeholder { - color: ${getFieldPlaceholderColorFactoryForState('focus')}; - } - } + ${inputStyle} } > .Element-IconBox { diff --git a/src/index.ts b/src/index.ts index 2b07e89f..9bdcf5db 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,6 +51,7 @@ export { MultiSelect } from './fields/MultiSelect' export { MultiRadio } from './fields/MultiRadio' export { MultiZoneEditor } from './fields/MultiZoneEditor' export { NumberInput } from './fields/NumberInput' +export { PhoneInput } from './fields/Phone/PhoneInput' export { Radio } from './fields/Radio' export { RichBooleanCheckbox } from './fields/RichBooleanCheckbox' export { Search } from './fields/Search' @@ -230,6 +231,7 @@ export type { MultiSelectProps } from './fields/MultiSelect' export type { MultiRadioProps } from './fields/MultiRadio' export type { MultiZoneEditorProps } from './fields/MultiZoneEditor' export type { NumberInputProps } from './fields/NumberInput' +export type { PhoneInputProps } from './fields/Phone/PhoneInput' export type { RichBooleanCheckboxProps } from './fields/RichBooleanCheckbox' export type { RadioProps } from './fields/Radio' export type { SearchProps } from './fields/Search' diff --git a/stories/fields/PhoneInput.stories.tsx b/stories/fields/PhoneInput.stories.tsx new file mode 100644 index 00000000..f6686a9d --- /dev/null +++ b/stories/fields/PhoneInput.stories.tsx @@ -0,0 +1,68 @@ +import { useState } from 'react' + +import { Output } from '../../.storybook/components/Output' +import { ARG_TYPE } from '../../.storybook/constants' +import { generateStoryDecorator } from '../../.storybook/utils/generateStoryDecorator' +import { PhoneInput, useFieldControl } from '../../src' + +import type { PhoneInputProps } from '../../src' +import type { Meta } from '@storybook/react' + +/* eslint-disable sort-keys-fix/sort-keys-fix */ +const meta: Meta = { + title: 'Fields/PhoneInput', + component: PhoneInput, + + argTypes: { + // disabled: ARG_TYPE.OPTIONAL_BOOLEAN, + // error: ARG_TYPE.OPTIONAL_STRING, + // isErrorMessageHidden: ARG_TYPE.OPTIONAL_BOOLEAN, + // isLabelHidden: ARG_TYPE.OPTIONAL_BOOLEAN, + // isLight: ARG_TYPE.OPTIONAL_BOOLEAN, + // isRequired: ARG_TYPE.OPTIONAL_BOOLEAN, + // isTransparent: ARG_TYPE.OPTIONAL_BOOLEAN, + // isUndefinedWhenDisabled: ARG_TYPE.OPTIONAL_BOOLEAN, + onChange: ARG_TYPE.NO_CONTROL_INPUT, + // readOnly: ARG_TYPE.OPTIONAL_BOOLEAN, + value: ARG_TYPE.OPTIONAL_STRING + }, + + args: { + // disabled: false, + // error: '', + // isErrorMessageHidden: false, + // isLabelHidden: false, + // isLight: false, + // isRequired: true, + // isTransparent: false, + // isUndefinedWhenDisabled: false, + label: 'A phone number input' + // name: 'myNumberInput', + // placeholder: 'Pick a number', + // readOnly: false + }, + + decorators: [ + generateStoryDecorator({ + box: { width: 640 }, + withBackgroundButton: true + }) + ] +} +/* eslint-enable sort-keys-fix/sort-keys-fix */ + +export default meta + +export function _PhoneInput(props: PhoneInputProps) { + const [outputValue, setOutputValue] = useState('∅') + + const { controlledOnChange, controlledValue } = useFieldControl(props.value, setOutputValue, '' as any) + + return ( + <> + + + {outputValue !== '∅' && } + + ) +}