@loja-integrada/admin-components 0.16.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Forms/Dropdown/Dropdown.d.ts +32 -3
- package/dist/Forms/FloatingLabelInput/FloatingLabelInput.d.ts +10 -0
- package/dist/Forms/FloatingLabelInput/FloatingLabelInput.spec.d.ts +1 -0
- package/dist/Forms/FloatingLabelInput/FloatingLabelInput.stories.d.ts +10 -0
- package/dist/Forms/FloatingLabelInput/index.d.ts +1 -0
- package/dist/Forms/FloatingLabelInputCurrency/FloatingLabelInputCurrency.d.ts +49 -0
- package/dist/Forms/FloatingLabelInputCurrency/FloatingLabelInputCurrency.spec.d.ts +1 -0
- package/dist/Forms/FloatingLabelInputCurrency/FloatingLabelInputCurrency.stories.d.ts +10 -0
- package/dist/Forms/FloatingLabelInputCurrency/index.d.ts +1 -0
- package/dist/Forms/FloatingLabelInputMask/FloatingLabelInputMask.d.ts +18 -0
- package/dist/Forms/FloatingLabelInputMask/FloatingLabelInputMask.spec.d.ts +1 -0
- package/dist/Forms/FloatingLabelInputMask/FloatingLabelInputMask.stories.d.ts +19 -0
- package/dist/Forms/FloatingLabelInputMask/index.d.ts +1 -0
- package/dist/Forms/index.d.ts +3 -0
- package/dist/Icons/icons-path/Camera.d.ts +2 -0
- package/dist/Icons/icons-path/Lightbulb.d.ts +2 -0
- package/dist/Icons/icons-path/Nav.d.ts +2 -0
- package/dist/Icons/icons-path/ShoppingCart.d.ts +2 -0
- package/dist/Icons/icons-path/index.d.ts +4 -0
- package/dist/Indicators/InformationBox/InformationBox.d.ts +25 -0
- package/dist/Indicators/InformationBox/InformationBox.spec.d.ts +1 -0
- package/dist/Indicators/InformationBox/InformationBoxt.stories.d.ts +4 -0
- package/dist/Indicators/InformationBox/index.d.ts +1 -0
- package/dist/Indicators/index.d.ts +1 -0
- package/dist/admin-components.cjs.development.js +401 -15
- package/dist/admin-components.cjs.development.js.map +1 -1
- package/dist/admin-components.cjs.production.min.js +1 -1
- package/dist/admin-components.cjs.production.min.js.map +1 -1
- package/dist/admin-components.esm.js +396 -16
- package/dist/admin-components.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/Forms/Dropdown/Dropdown.tsx +50 -15
- package/src/Forms/FloatingLabelInput/FloatingLabelInput.spec.tsx +25 -0
- package/src/Forms/FloatingLabelInput/FloatingLabelInput.stories.tsx +72 -0
- package/src/Forms/FloatingLabelInput/FloatingLabelInput.tsx +80 -0
- package/src/Forms/FloatingLabelInput/index.ts +1 -0
- package/src/Forms/FloatingLabelInputCurrency/FloatingLabelInputCurrency.spec.tsx +15 -0
- package/src/Forms/FloatingLabelInputCurrency/FloatingLabelInputCurrency.stories.tsx +72 -0
- package/src/Forms/FloatingLabelInputCurrency/FloatingLabelInputCurrency.tsx +251 -0
- package/src/Forms/FloatingLabelInputCurrency/index.ts +1 -0
- package/src/Forms/FloatingLabelInputMask/FloatingLabelInputMask.spec.tsx +65 -0
- package/src/Forms/FloatingLabelInputMask/FloatingLabelInputMask.stories.tsx +148 -0
- package/src/Forms/FloatingLabelInputMask/FloatingLabelInputMask.tsx +56 -0
- package/src/Forms/FloatingLabelInputMask/index.ts +1 -0
- package/src/Forms/index.ts +3 -0
- package/src/Icons/icons-path/Camera.tsx +9 -0
- package/src/Icons/icons-path/Lightbulb.tsx +9 -0
- package/src/Icons/icons-path/Nav.tsx +11 -0
- package/src/Icons/icons-path/ShoppingCart.tsx +11 -0
- package/src/Icons/icons-path/index.ts +8 -0
- package/src/Indicators/InformationBox/InformationBox.spec.tsx +27 -0
- package/src/Indicators/InformationBox/InformationBox.tsx +86 -0
- package/src/Indicators/InformationBox/InformationBoxt.stories.tsx +24 -0
- package/src/Indicators/InformationBox/index.tsx +1 -0
- package/src/Indicators/index.ts +1 -0
package/package.json
CHANGED
|
@@ -19,32 +19,49 @@ export const sizeClasses = {
|
|
|
19
19
|
xlarge: 'h-24',
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export const valueFontSizesClasses = {
|
|
23
|
+
default: 'text-f6',
|
|
24
|
+
small: 'text-f7',
|
|
25
|
+
large: 'text-f5',
|
|
26
|
+
xlarge: 'text-f4',
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
export const variantControlClasses = {
|
|
23
|
-
default: 'border-card-stroke rounded',
|
|
30
|
+
default: 'border-card-stroke rounded pl-2',
|
|
24
31
|
secondary:
|
|
25
|
-
'border-inverted-2 hover:bg-base-3 rounded-md focus:ring-2 focus:ring-focus focus:ring-offset-2 focus:bg-base-1',
|
|
32
|
+
'border-inverted-2 hover:bg-base-3 rounded-md focus:ring-2 focus:ring-focus focus:ring-offset-2 focus:bg-base-1 pl-2',
|
|
33
|
+
simple: 'border-none',
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
export const variantValueClasses = {
|
|
29
|
-
default: 'font-normal',
|
|
30
|
-
secondary: 'font-semibold',
|
|
37
|
+
default: 'font-normal text-on-base',
|
|
38
|
+
secondary: 'font-semibold text-on-base',
|
|
39
|
+
simple: 'font-normal text-on-base',
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export const variantSelectedClasses = {
|
|
34
43
|
default: '',
|
|
35
44
|
secondary: 'bg-base-1 shadow-inner',
|
|
45
|
+
simple: 'bg-transparent',
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
export const variantDisabledClasses = {
|
|
39
49
|
default: 'cursor-not-allowed opacity-70',
|
|
40
50
|
secondary: 'cursor-not-allowed opacity-90 bg-base-4 border-base-4',
|
|
51
|
+
simple: 'cursor-not-allowed opacity-70',
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
export const varianErrorClasses = {
|
|
44
55
|
default: 'border-danger rounded',
|
|
45
56
|
secondary: 'border-danger rounded-md',
|
|
57
|
+
simple: '',
|
|
46
58
|
}
|
|
47
59
|
|
|
60
|
+
export const valueContainerStyles = {
|
|
61
|
+
default: { paddingLeft: 8 },
|
|
62
|
+
secondary: { paddingLeft: 8 },
|
|
63
|
+
simple: { paddingLeft: 0 },
|
|
64
|
+
}
|
|
48
65
|
export interface CustomOptionProps {
|
|
49
66
|
value: string | number
|
|
50
67
|
label: string
|
|
@@ -57,7 +74,9 @@ export interface CustomGroupedOptionsProps {
|
|
|
57
74
|
options: CustomOptionProps[]
|
|
58
75
|
}
|
|
59
76
|
|
|
60
|
-
export type DropdownVariant = 'default' | 'secondary'
|
|
77
|
+
export type DropdownVariant = 'default' | 'secondary' | 'simple'
|
|
78
|
+
export type DropdownSizes = keyof typeof sizeClasses
|
|
79
|
+
export type DropdownValueFontSizes = keyof typeof valueFontSizesClasses
|
|
61
80
|
|
|
62
81
|
const {
|
|
63
82
|
Option,
|
|
@@ -90,11 +109,11 @@ const CustomControl = (
|
|
|
90
109
|
props: React.PropsWithChildren<
|
|
91
110
|
ControlProps<CustomOptionProps, false, GroupTypeBase<CustomOptionProps>>
|
|
92
111
|
>,
|
|
93
|
-
size:
|
|
112
|
+
size: DropdownSizes,
|
|
94
113
|
variant: DropdownVariant,
|
|
95
114
|
errorMessage?: string
|
|
96
115
|
) => {
|
|
97
|
-
const controlClasses = `cursor-pointer transition-all flex itens-center border
|
|
116
|
+
const controlClasses = `cursor-pointer transition-all flex itens-center border ${
|
|
98
117
|
errorMessage ? varianErrorClasses[variant] : variantControlClasses[variant]
|
|
99
118
|
} ${sizeClasses[size]} ${
|
|
100
119
|
props.menuIsOpen ? variantSelectedClasses[variant] : ''
|
|
@@ -155,18 +174,23 @@ const CustomPlaceholder = (
|
|
|
155
174
|
CustomOptionProps,
|
|
156
175
|
false,
|
|
157
176
|
GroupTypeBase<CustomOptionProps>
|
|
158
|
-
|
|
177
|
+
>,
|
|
178
|
+
fontSize: DropdownValueFontSizes
|
|
159
179
|
) => (
|
|
160
|
-
<Placeholder
|
|
180
|
+
<Placeholder
|
|
181
|
+
{...props}
|
|
182
|
+
className={`tracking-4 w-full pr-2 truncate ${valueFontSizesClasses[fontSize]}`}
|
|
183
|
+
/>
|
|
161
184
|
)
|
|
162
185
|
|
|
163
186
|
const CustomSingleValue = (
|
|
164
187
|
props: SingleValueProps<CustomOptionProps, GroupTypeBase<CustomOptionProps>>,
|
|
165
|
-
variant: DropdownVariant
|
|
188
|
+
variant: DropdownVariant,
|
|
189
|
+
fontSize: DropdownValueFontSizes
|
|
166
190
|
) => (
|
|
167
191
|
<SingleValue
|
|
168
192
|
{...props}
|
|
169
|
-
className={`
|
|
193
|
+
className={`tracking-4 truncate ${variantValueClasses[variant]} ${valueFontSizesClasses[fontSize]}`}
|
|
170
194
|
/>
|
|
171
195
|
)
|
|
172
196
|
|
|
@@ -197,6 +221,7 @@ const DropdownComponent = (
|
|
|
197
221
|
menuPlacement = 'auto',
|
|
198
222
|
menuWidth = '100%',
|
|
199
223
|
menuHorizontalPlacement,
|
|
224
|
+
valueFontSize = 'default',
|
|
200
225
|
}: DropdownProps,
|
|
201
226
|
ref: React.ForwardedRef<any>
|
|
202
227
|
) => {
|
|
@@ -212,6 +237,7 @@ const DropdownComponent = (
|
|
|
212
237
|
className="mb-1"
|
|
213
238
|
/>
|
|
214
239
|
<Select
|
|
240
|
+
id={inputId}
|
|
215
241
|
ref={ref}
|
|
216
242
|
className={`w-full text-inverted-2`}
|
|
217
243
|
classNamePrefix="select"
|
|
@@ -258,6 +284,9 @@ const DropdownComponent = (
|
|
|
258
284
|
maxWidth: 'calc(100% - 6px)',
|
|
259
285
|
}
|
|
260
286
|
},
|
|
287
|
+
valueContainer: (base) => {
|
|
288
|
+
return { ...base, ...valueContainerStyles[variant] }
|
|
289
|
+
},
|
|
261
290
|
input: (base) => {
|
|
262
291
|
return {
|
|
263
292
|
...base,
|
|
@@ -275,8 +304,9 @@ const DropdownComponent = (
|
|
|
275
304
|
DropdownIndicator: (props) => CustomDropdownIndicator(props),
|
|
276
305
|
Control: (props) => CustomControl(props, size, variant, errorMessage),
|
|
277
306
|
GroupHeading: (props) => CustomGroupHeading(props),
|
|
278
|
-
Placeholder: (props) => CustomPlaceholder(props),
|
|
279
|
-
SingleValue: (props) =>
|
|
307
|
+
Placeholder: (props) => CustomPlaceholder(props, valueFontSize),
|
|
308
|
+
SingleValue: (props) =>
|
|
309
|
+
CustomSingleValue(props, variant, valueFontSize),
|
|
280
310
|
}}
|
|
281
311
|
/>
|
|
282
312
|
<InputHelpText
|
|
@@ -303,9 +333,14 @@ export interface DropdownProps {
|
|
|
303
333
|
* Changes the size of dropdown
|
|
304
334
|
* @default default
|
|
305
335
|
* */
|
|
306
|
-
size?:
|
|
336
|
+
size?: DropdownSizes
|
|
307
337
|
/**
|
|
308
|
-
* Changes the size of
|
|
338
|
+
* Changes the size of selected value
|
|
339
|
+
* @default default
|
|
340
|
+
* */
|
|
341
|
+
valueFontSize?: DropdownValueFontSizes
|
|
342
|
+
/**
|
|
343
|
+
* Changes the variant of dropdown
|
|
309
344
|
* @default default
|
|
310
345
|
* */
|
|
311
346
|
variant?: DropdownVariant
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { composeStories } from '@storybook/testing-react'
|
|
3
|
+
import { mount } from '@cypress/react'
|
|
4
|
+
import * as stories from './FloatingLabelInput.stories'
|
|
5
|
+
|
|
6
|
+
const { Default, Prefix, IconPrefix, WithBoth } = composeStories(stories)
|
|
7
|
+
|
|
8
|
+
describe('Input tests', () => {
|
|
9
|
+
it('Default', () => {
|
|
10
|
+
mount(<Default />)
|
|
11
|
+
const val = 'Preencher campo'
|
|
12
|
+
cy.get('input').type(val).should('have.value', val)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('Sufix and Prefix', () => {
|
|
16
|
+
mount(<Prefix />)
|
|
17
|
+
cy.get('.adornment').contains('R$')
|
|
18
|
+
|
|
19
|
+
mount(<IconPrefix />)
|
|
20
|
+
cy.get('svg').should('have.class', 'icon-cog').parent('label')
|
|
21
|
+
|
|
22
|
+
mount(<WithBoth />)
|
|
23
|
+
cy.get('.adornment').should('have.length', 2)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Story, Meta } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import { Icon } from '../../Icons'
|
|
5
|
+
import {
|
|
6
|
+
FloatingLabelInput,
|
|
7
|
+
FloatingLabelInputProps,
|
|
8
|
+
} from './FloatingLabelInput'
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
title: 'Forms/FloatingLabelInput',
|
|
12
|
+
component: FloatingLabelInput,
|
|
13
|
+
} as Meta
|
|
14
|
+
|
|
15
|
+
const Template: Story<FloatingLabelInputProps> = (args) => (
|
|
16
|
+
<FloatingLabelInput {...args} />
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export const Default = Template.bind({})
|
|
20
|
+
Default.args = {
|
|
21
|
+
label: 'Meu Campo',
|
|
22
|
+
name: 'nome',
|
|
23
|
+
id: 'campo',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Error = Template.bind({})
|
|
27
|
+
Error.args = {
|
|
28
|
+
label: 'Meu Campo',
|
|
29
|
+
name: 'nome',
|
|
30
|
+
id: 'campo',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Prefix = Template.bind({})
|
|
34
|
+
Prefix.args = {
|
|
35
|
+
label: 'Meu Campo',
|
|
36
|
+
prefix: 'R$',
|
|
37
|
+
name: 'valor',
|
|
38
|
+
id: 'campo',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const IconPrefix = Template.bind({})
|
|
42
|
+
IconPrefix.args = {
|
|
43
|
+
label: 'Meu Campo',
|
|
44
|
+
prefix: <Icon icon="cog" />,
|
|
45
|
+
name: 'nome',
|
|
46
|
+
id: 'campo',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const Sufix = Template.bind({})
|
|
50
|
+
Sufix.args = {
|
|
51
|
+
label: 'Meu Campo',
|
|
52
|
+
sufix: 'R$',
|
|
53
|
+
name: 'valor',
|
|
54
|
+
id: 'campo',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const IconSufix = Template.bind({})
|
|
58
|
+
IconSufix.args = {
|
|
59
|
+
label: 'Meu Campo',
|
|
60
|
+
sufix: <Icon icon="cog" />,
|
|
61
|
+
name: 'nome',
|
|
62
|
+
id: 'campo',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const WithBoth = Template.bind({})
|
|
66
|
+
WithBoth.args = {
|
|
67
|
+
label: 'Meu Campo',
|
|
68
|
+
prefix: 'R$',
|
|
69
|
+
sufix: <Icon icon="cog" />,
|
|
70
|
+
name: 'valor',
|
|
71
|
+
id: 'campo',
|
|
72
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
const FloatingLabelInputComponent = (
|
|
4
|
+
{
|
|
5
|
+
prefix,
|
|
6
|
+
sufix,
|
|
7
|
+
id,
|
|
8
|
+
type = 'text',
|
|
9
|
+
placeholder = ' ',
|
|
10
|
+
label,
|
|
11
|
+
showLabel = false,
|
|
12
|
+
className = '',
|
|
13
|
+
...props
|
|
14
|
+
}: FloatingLabelInputProps,
|
|
15
|
+
ref: React.ForwardedRef<HTMLInputElement>
|
|
16
|
+
) => {
|
|
17
|
+
const peerFocusLabelClasses = `peer-focus:text-f7 peer-focus:-translate-y-4 ${
|
|
18
|
+
prefix ? 'peer-focus:pl-0' : ''
|
|
19
|
+
} `
|
|
20
|
+
const peerPlaceholderShowLabelClasses = `${
|
|
21
|
+
prefix ? 'peer-placeholder-shown:pl-8' : ''
|
|
22
|
+
} peer-placeholder-shown:translate-y-0 peer-placeholder-shown:text-f4 `
|
|
23
|
+
const labelClassName = `cursor-text absolute text-f7 text-inverted-2 duration-300 transform -translate-y-4 top-5 z-10 origin-[0] ${peerFocusLabelClasses} ${peerPlaceholderShowLabelClasses}`
|
|
24
|
+
|
|
25
|
+
const inputClassName = `block text-f4 ${prefix ? 'pl-7' : ''} ${
|
|
26
|
+
sufix ? 'pr-6' : ''
|
|
27
|
+
} w-full border-none appearance-none peer focus:outline-none bg-transparent pb-2.5 pt-5 ${
|
|
28
|
+
className ? className : ''
|
|
29
|
+
}`
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="relative">
|
|
33
|
+
{prefix && (
|
|
34
|
+
<label
|
|
35
|
+
htmlFor={id}
|
|
36
|
+
className="adornment absolute left-0 top-5 w-8 h-8 flex items-center justify-start"
|
|
37
|
+
>
|
|
38
|
+
{prefix}
|
|
39
|
+
</label>
|
|
40
|
+
)}
|
|
41
|
+
<input
|
|
42
|
+
ref={ref}
|
|
43
|
+
type={type}
|
|
44
|
+
id={id}
|
|
45
|
+
className={inputClassName}
|
|
46
|
+
placeholder={placeholder}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
{showLabel && (
|
|
50
|
+
<label htmlFor={id} className={labelClassName}>
|
|
51
|
+
{label}
|
|
52
|
+
</label>
|
|
53
|
+
)}
|
|
54
|
+
{sufix && (
|
|
55
|
+
<label
|
|
56
|
+
htmlFor={id}
|
|
57
|
+
className="adornment absolute right-0 top-5 w-8 h-8 flex items-center justify-end"
|
|
58
|
+
>
|
|
59
|
+
{sufix}
|
|
60
|
+
</label>
|
|
61
|
+
)}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const InputWithFowardRef = React.forwardRef(FloatingLabelInputComponent)
|
|
67
|
+
export const FloatingLabelInput = React.memo(InputWithFowardRef)
|
|
68
|
+
|
|
69
|
+
export interface FloatingLabelInputProps
|
|
70
|
+
extends Omit<
|
|
71
|
+
React.InputHTMLAttributes<HTMLInputElement>,
|
|
72
|
+
'prefix' | 'sufix'
|
|
73
|
+
> {
|
|
74
|
+
prefix?: React.ReactNode
|
|
75
|
+
sufix?: React.ReactNode
|
|
76
|
+
label?: string
|
|
77
|
+
showLabel?: boolean
|
|
78
|
+
className?: string
|
|
79
|
+
textArea?: false
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FloatingLabelInput'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { composeStories } from '@storybook/testing-react'
|
|
3
|
+
import { mount } from '@cypress/react'
|
|
4
|
+
import * as stories from './FloatingLabelInputCurrency.stories'
|
|
5
|
+
|
|
6
|
+
const { Default } = composeStories(stories)
|
|
7
|
+
|
|
8
|
+
describe('FloatingLabelInputCurrency tests', () => {
|
|
9
|
+
it('Default', () => {
|
|
10
|
+
mount(<Default />)
|
|
11
|
+
const val = '456.88'
|
|
12
|
+
const valMasked = '456,88'
|
|
13
|
+
cy.get('input').type(val).should('have.value', valMasked)
|
|
14
|
+
})
|
|
15
|
+
})
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Story, Meta } from '@storybook/react'
|
|
3
|
+
|
|
4
|
+
import { Icon } from '../../Icons'
|
|
5
|
+
import {
|
|
6
|
+
FloatingLabelInputCurrency,
|
|
7
|
+
FloatingLabelInputCurrencyProps,
|
|
8
|
+
} from './FloatingLabelInputCurrency'
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
title: 'Forms/FloatingLabelInputCurrency',
|
|
12
|
+
component: FloatingLabelInputCurrency,
|
|
13
|
+
} as Meta
|
|
14
|
+
|
|
15
|
+
const Template: Story<FloatingLabelInputCurrencyProps> = (args) => (
|
|
16
|
+
<FloatingLabelInputCurrency {...args} />
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export const Default = Template.bind({})
|
|
20
|
+
Default.args = {
|
|
21
|
+
label: 'Meu Campo',
|
|
22
|
+
name: 'nome',
|
|
23
|
+
id: 'campo',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const Error = Template.bind({})
|
|
27
|
+
Error.args = {
|
|
28
|
+
label: 'Meu Campo',
|
|
29
|
+
name: 'nome',
|
|
30
|
+
id: 'campo',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Prefix = Template.bind({})
|
|
34
|
+
Prefix.args = {
|
|
35
|
+
label: 'Meu Campo',
|
|
36
|
+
prefix: 'R$',
|
|
37
|
+
name: 'valor',
|
|
38
|
+
id: 'campo',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const IconPrefix = Template.bind({})
|
|
42
|
+
IconPrefix.args = {
|
|
43
|
+
label: 'Meu Campo',
|
|
44
|
+
prefix: <Icon icon="cog" />,
|
|
45
|
+
name: 'nome',
|
|
46
|
+
id: 'campo',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const Sufix = Template.bind({})
|
|
50
|
+
Sufix.args = {
|
|
51
|
+
label: 'Meu Campo',
|
|
52
|
+
sufix: 'R$',
|
|
53
|
+
name: 'valor',
|
|
54
|
+
id: 'campo',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const IconSufix = Template.bind({})
|
|
58
|
+
IconSufix.args = {
|
|
59
|
+
label: 'Meu Campo',
|
|
60
|
+
sufix: <Icon icon="cog" />,
|
|
61
|
+
name: 'nome',
|
|
62
|
+
id: 'campo',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const WithBoth = Template.bind({})
|
|
66
|
+
WithBoth.args = {
|
|
67
|
+
label: 'Meu Campo',
|
|
68
|
+
prefix: 'R$',
|
|
69
|
+
sufix: <Icon icon="cog" />,
|
|
70
|
+
name: 'valor',
|
|
71
|
+
id: 'campo',
|
|
72
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import { composeRefs } from '../../utils'
|
|
3
|
+
import { FloatingLabelInput } from '../FloatingLabelInput/FloatingLabelInput'
|
|
4
|
+
import {
|
|
5
|
+
defaultIntlConfig,
|
|
6
|
+
defaultMaxValue,
|
|
7
|
+
formatCurrency,
|
|
8
|
+
} from '../InputCurrency/utils'
|
|
9
|
+
|
|
10
|
+
const FloatingLabelInputCurrencyComponent = (
|
|
11
|
+
{
|
|
12
|
+
value,
|
|
13
|
+
defaultValue,
|
|
14
|
+
config = defaultIntlConfig,
|
|
15
|
+
currency = 'BRL',
|
|
16
|
+
max = defaultMaxValue,
|
|
17
|
+
autoFocus = false,
|
|
18
|
+
autoSelect = false,
|
|
19
|
+
autoReset = false,
|
|
20
|
+
onChange = () => null,
|
|
21
|
+
onBlur = () => null,
|
|
22
|
+
onFocus = () => null,
|
|
23
|
+
onKeyPress = () => null,
|
|
24
|
+
prefix,
|
|
25
|
+
sufix,
|
|
26
|
+
id,
|
|
27
|
+
type = 'text',
|
|
28
|
+
placeholder = ' ',
|
|
29
|
+
label,
|
|
30
|
+
showLabel = true,
|
|
31
|
+
...props
|
|
32
|
+
}: FloatingLabelInputCurrencyProps,
|
|
33
|
+
ref: React.ForwardedRef<HTMLInputElement>
|
|
34
|
+
) => {
|
|
35
|
+
config = config[currency]
|
|
36
|
+
|
|
37
|
+
max = Number(max)
|
|
38
|
+
|
|
39
|
+
const inputRef = useCallback(
|
|
40
|
+
(node) => {
|
|
41
|
+
const isActive = node === document.activeElement
|
|
42
|
+
if (node && autoFocus && !isActive) {
|
|
43
|
+
node.focus()
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
[autoFocus]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const [maskedValue, setMaskedValue] = useState<string | number>('0')
|
|
50
|
+
|
|
51
|
+
// to prevent a malformed config object
|
|
52
|
+
const safeConfig = useMemo(
|
|
53
|
+
() => () => {
|
|
54
|
+
const {
|
|
55
|
+
format: { maximumFractionDigits },
|
|
56
|
+
} = config
|
|
57
|
+
const finalConfig = {
|
|
58
|
+
...defaultIntlConfig[currency],
|
|
59
|
+
...config,
|
|
60
|
+
}
|
|
61
|
+
// at the moment this prevents problems when converting numbers
|
|
62
|
+
// with zeroes in-between, otherwise 205 would convert to 25.
|
|
63
|
+
finalConfig.format.minimumFractionDigits = maximumFractionDigits
|
|
64
|
+
return finalConfig
|
|
65
|
+
},
|
|
66
|
+
[currency, config]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const clean = (number: any) => {
|
|
70
|
+
if (typeof number === 'number') {
|
|
71
|
+
return number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// strips everything that is not a number (positive or negative)
|
|
75
|
+
return Number(number.toString().replace(/[^0-9-]/g, ''))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const normalizeValue = (number: any) => {
|
|
79
|
+
const {
|
|
80
|
+
format: { maximumFractionDigits },
|
|
81
|
+
} = safeConfig()
|
|
82
|
+
let safeNumber = number
|
|
83
|
+
|
|
84
|
+
if (typeof number === 'string') {
|
|
85
|
+
safeNumber = clean(number)
|
|
86
|
+
|
|
87
|
+
if (safeNumber % 1 !== 0) {
|
|
88
|
+
safeNumber = safeNumber.toFixed(maximumFractionDigits)
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// all input numbers must be a float point (for the cents portion). This is a fallback in case of integer ones.
|
|
92
|
+
safeNumber = Number.isInteger(number)
|
|
93
|
+
? Number(number) * 10 ** maximumFractionDigits
|
|
94
|
+
: number.toFixed(maximumFractionDigits)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// divide it by 10 power the maximum fraction digits.
|
|
98
|
+
return clean(safeNumber) / 10 ** maximumFractionDigits
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const calculateValues = (inputFieldValue: any) => {
|
|
102
|
+
const value = normalizeValue(inputFieldValue)
|
|
103
|
+
const maskedValue = formatCurrency(value, safeConfig())
|
|
104
|
+
|
|
105
|
+
return [value, maskedValue]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const updateValues = (value: any) => {
|
|
109
|
+
const [calculatedValue, calculatedMaskedValue] = calculateValues(value)
|
|
110
|
+
|
|
111
|
+
if ((!max && max !== 0) || calculatedValue <= max) {
|
|
112
|
+
setMaskedValue(calculatedMaskedValue)
|
|
113
|
+
return [calculatedValue, calculatedMaskedValue]
|
|
114
|
+
} else {
|
|
115
|
+
const [maxCalculatedValue, maxCalculatedMaskedValue] =
|
|
116
|
+
calculateValues(max)
|
|
117
|
+
setMaskedValue(maxCalculatedMaskedValue)
|
|
118
|
+
return [maxCalculatedValue, maxCalculatedMaskedValue]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const handleChange = (event: any) => {
|
|
123
|
+
event.preventDefault()
|
|
124
|
+
|
|
125
|
+
const [value, maskedValue] = updateValues(event.target.value)
|
|
126
|
+
|
|
127
|
+
if (maskedValue) {
|
|
128
|
+
onChange(event, value, maskedValue)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const handleBlur = (event: any) => {
|
|
133
|
+
const [value, maskedValue] = updateValues(event.target.value)
|
|
134
|
+
if (autoReset) {
|
|
135
|
+
calculateValues(0)
|
|
136
|
+
}
|
|
137
|
+
if (maskedValue) {
|
|
138
|
+
onBlur(event, value, maskedValue)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const handleFocus = (event: any) => {
|
|
143
|
+
if (autoSelect) {
|
|
144
|
+
event.target.select()
|
|
145
|
+
}
|
|
146
|
+
const [value, maskedValue] = updateValues(event.target.value)
|
|
147
|
+
if (maskedValue) {
|
|
148
|
+
onFocus(event, value, maskedValue)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const handleKeyUp = (event: any) =>
|
|
153
|
+
onKeyPress(event, event.key, event.keyCode)
|
|
154
|
+
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
const currentValue = value || defaultValue || 0
|
|
157
|
+
const [, maskedValue] = calculateValues(currentValue)
|
|
158
|
+
setMaskedValue(maskedValue)
|
|
159
|
+
// eslint-disable-next-line
|
|
160
|
+
}, [currency, value, defaultValue, config])
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<FloatingLabelInput
|
|
164
|
+
{...props}
|
|
165
|
+
type={type}
|
|
166
|
+
id={id}
|
|
167
|
+
placeholder={placeholder}
|
|
168
|
+
onBlur={handleBlur}
|
|
169
|
+
onChange={handleChange}
|
|
170
|
+
onKeyUp={handleKeyUp}
|
|
171
|
+
onFocus={handleFocus}
|
|
172
|
+
ref={composeRefs(inputRef, ref)}
|
|
173
|
+
value={maskedValue}
|
|
174
|
+
inputMode="decimal"
|
|
175
|
+
label={label}
|
|
176
|
+
showLabel={showLabel}
|
|
177
|
+
prefix={prefix}
|
|
178
|
+
sufix={sufix}
|
|
179
|
+
/>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const InputWithFowardRef = React.forwardRef(FloatingLabelInputCurrencyComponent)
|
|
184
|
+
export const FloatingLabelInputCurrency = React.memo(InputWithFowardRef)
|
|
185
|
+
|
|
186
|
+
export interface FloatingLabelInputCurrencyProps
|
|
187
|
+
extends Omit<
|
|
188
|
+
React.InputHTMLAttributes<HTMLInputElement>,
|
|
189
|
+
'prefix' | 'sufix' | 'onChange' | 'onBlur' | 'onFocus' | 'onKeyPress'
|
|
190
|
+
> {
|
|
191
|
+
prefix?: React.ReactNode
|
|
192
|
+
sufix?: React.ReactNode
|
|
193
|
+
label?: string
|
|
194
|
+
showLabel?: boolean
|
|
195
|
+
/** Currency to format value to
|
|
196
|
+
* @default 'BRL'
|
|
197
|
+
* */
|
|
198
|
+
currency?: keyof typeof defaultIntlConfig
|
|
199
|
+
/** Max value allowed
|
|
200
|
+
* @default 1000000000000000
|
|
201
|
+
* */
|
|
202
|
+
max?: number | string
|
|
203
|
+
/** Custom Intl config
|
|
204
|
+
* @deprecated Don't use, component already has its default config
|
|
205
|
+
* */
|
|
206
|
+
config?: any
|
|
207
|
+
/** Should auto focus
|
|
208
|
+
* @default false
|
|
209
|
+
* */
|
|
210
|
+
autoFocus?: boolean
|
|
211
|
+
/** Should auto select
|
|
212
|
+
* @default false
|
|
213
|
+
* */
|
|
214
|
+
autoSelect?: boolean
|
|
215
|
+
/** Should auto reset
|
|
216
|
+
* @default false
|
|
217
|
+
* */
|
|
218
|
+
autoReset?: boolean
|
|
219
|
+
/**
|
|
220
|
+
* onChange with additional params: (event, value, maskedValue)
|
|
221
|
+
* */
|
|
222
|
+
onChange?: (
|
|
223
|
+
arg0: InputEvent,
|
|
224
|
+
arg1: string | number,
|
|
225
|
+
arg2: string | number
|
|
226
|
+
) => void
|
|
227
|
+
/**
|
|
228
|
+
* onBlur with additional params: (event, value, maskedValue)
|
|
229
|
+
* */
|
|
230
|
+
onBlur?: (
|
|
231
|
+
arg0: InputEvent,
|
|
232
|
+
arg1: string | number,
|
|
233
|
+
arg2: string | number
|
|
234
|
+
) => void
|
|
235
|
+
/**
|
|
236
|
+
* onFocus with additional params: (event, value, maskedValue)
|
|
237
|
+
* */
|
|
238
|
+
onFocus?: (
|
|
239
|
+
arg0: InputEvent,
|
|
240
|
+
arg1: string | number,
|
|
241
|
+
arg2: string | number
|
|
242
|
+
) => void
|
|
243
|
+
/**
|
|
244
|
+
* onKeyPress with additional params: (event, key, keyCode)
|
|
245
|
+
* */
|
|
246
|
+
onKeyPress?: (
|
|
247
|
+
arg0: InputEvent,
|
|
248
|
+
arg1: string | number,
|
|
249
|
+
arg2: string | number
|
|
250
|
+
) => void
|
|
251
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FloatingLabelInputCurrency'
|