buildgrid-ui 1.1.0-dev.7 → 1.1.0-dev.8
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/buildgrid-ui.es.js +1236 -1155
- package/dist/buildgrid-ui.umd.js +24 -24
- package/dist/components/currency-input/currency-input.d.ts +10 -0
- package/dist/components/currency-input/index.d.ts +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/utils/formatters.d.ts +8 -0
- package/package.json +1 -1
- package/src/components/currency-input/currency-input.stories.tsx +28 -0
- package/src/components/currency-input/currency-input.tsx +93 -0
- package/src/components/currency-input/index.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/lib/index.ts +1 -0
- package/src/lib/utils/formatters.ts +55 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AdaptiveInputProps } from '../adaptative-input';
|
|
3
|
+
export interface CurrencyInputProps extends AdaptiveInputProps {
|
|
4
|
+
currencySymbol?: string;
|
|
5
|
+
decimalSeparator?: string;
|
|
6
|
+
thousandSeparator?: string;
|
|
7
|
+
onValueChange: (value: number) => void;
|
|
8
|
+
}
|
|
9
|
+
declare const CurrencyInput: React.ForwardRefExoticComponent<CurrencyInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
10
|
+
export { CurrencyInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './currency-input';
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const formatCurrency: (number: number, language?: string, currency?: string) => string;
|
|
2
|
+
export declare const formatDateAndWeekday: (date: Date | string, language?: string) => string;
|
|
3
|
+
export declare const formatDateAndWeekdayAndYear: (date: Date | string, language?: string) => string;
|
|
4
|
+
export declare const formatDateAndMonth: (date: Date | string, language?: string) => string;
|
|
5
|
+
export declare const formatLongDate: (date: Date | string, language?: string) => string;
|
|
6
|
+
export declare const formatShortDate: (date: Date, language?: string) => string;
|
|
7
|
+
export declare const getMonthYearFromISODate: (date: string) => string;
|
|
8
|
+
export declare const formatWeekDayAndShortDate: (date: Date, language?: string) => string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// organize-imports-ignore
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
4
|
+
|
|
5
|
+
import { CurrencyInput } from './currency-input'
|
|
6
|
+
import { DollarSign } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof CurrencyInput> = {
|
|
9
|
+
title: 'Components/input/currency',
|
|
10
|
+
component: CurrencyInput,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default meta
|
|
14
|
+
type Story = StoryObj<typeof CurrencyInput>
|
|
15
|
+
|
|
16
|
+
const Template = () => {
|
|
17
|
+
const [value, setValue] = React.useState(0)
|
|
18
|
+
return (
|
|
19
|
+
<div className="w-96">
|
|
20
|
+
<CurrencyInput value={value} onValueChange={(number) => setValue(number)} />
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
render: Template.bind({}),
|
|
27
|
+
args: {},
|
|
28
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react'
|
|
2
|
+
import { AdaptiveInput, AdaptiveInputProps } from '../adaptative-input'
|
|
3
|
+
|
|
4
|
+
export interface CurrencyInputProps extends AdaptiveInputProps {
|
|
5
|
+
currencySymbol?: string
|
|
6
|
+
decimalSeparator?: string
|
|
7
|
+
thousandSeparator?: string
|
|
8
|
+
onValueChange: (value: number) => void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const CurrencyInput = React.forwardRef<HTMLInputElement, CurrencyInputProps>(
|
|
12
|
+
(props, ref) => {
|
|
13
|
+
const {
|
|
14
|
+
currencySymbol = '$',
|
|
15
|
+
decimalSeparator = '.',
|
|
16
|
+
thousandSeparator = ',',
|
|
17
|
+
onValueChange,
|
|
18
|
+
className,
|
|
19
|
+
value = 0,
|
|
20
|
+
...rest
|
|
21
|
+
} = props
|
|
22
|
+
|
|
23
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
24
|
+
|
|
25
|
+
useImperativeHandle(ref, () => inputRef.current as HTMLInputElement)
|
|
26
|
+
|
|
27
|
+
const [inputValue, setInputValue] = useState<string>('') // String to show in input
|
|
28
|
+
|
|
29
|
+
// Function to format the numeric value into a currency format
|
|
30
|
+
const formatNumber = (numericValue: number): string => {
|
|
31
|
+
const parts = numericValue.toFixed(2).split('.') // Ensure two decimal places
|
|
32
|
+
const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator) // Add thousand separators
|
|
33
|
+
return `${currencySymbol} ${integerPart}${decimalSeparator}${parts[1]}`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Handle input change
|
|
37
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
38
|
+
const rawValue = e.target.value
|
|
39
|
+
const numericValue = parseFloat(rawValue.replace(/\D/g, '')) / 100
|
|
40
|
+
|
|
41
|
+
// Update the input with formatted value
|
|
42
|
+
setInputValue(formatNumber(numericValue))
|
|
43
|
+
|
|
44
|
+
// Send the numeric value (without formatting) to the parent component
|
|
45
|
+
onValueChange(numericValue)
|
|
46
|
+
|
|
47
|
+
// Move the cursor to the end of the input value
|
|
48
|
+
if (inputRef.current) {
|
|
49
|
+
window.requestAnimationFrame(() => {
|
|
50
|
+
inputRef.current?.setSelectionRange(
|
|
51
|
+
inputRef.current.value.length,
|
|
52
|
+
inputRef.current.value.length,
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Prevent cursor from being set elsewhere except at the end
|
|
59
|
+
const handleFocus = () => {
|
|
60
|
+
if (inputRef.current) {
|
|
61
|
+
inputRef.current.setSelectionRange(
|
|
62
|
+
inputRef.current.value.length,
|
|
63
|
+
inputRef.current.value.length,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Set the initial formatted value when the component mounts or the value prop changes
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
setInputValue(formatNumber(+value))
|
|
71
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
72
|
+
}, [value])
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<AdaptiveInput
|
|
76
|
+
type="text"
|
|
77
|
+
inputMode="numeric"
|
|
78
|
+
value={inputValue}
|
|
79
|
+
ref={inputRef}
|
|
80
|
+
onChange={handleInputChange}
|
|
81
|
+
onFocus={handleFocus}
|
|
82
|
+
onClick={handleFocus} // Ensures that even if the user clicks, the cursor is forced to the end
|
|
83
|
+
placeholder={`${currencySymbol} 0${decimalSeparator}00`}
|
|
84
|
+
className={className}
|
|
85
|
+
{...rest}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
CurrencyInput.displayName = 'CurrencyInput'
|
|
92
|
+
|
|
93
|
+
export { CurrencyInput }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './currency-input'
|
package/src/components/index.ts
CHANGED
package/src/lib/index.ts
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const formatCurrency = (
|
|
2
|
+
number: number,
|
|
3
|
+
language = 'pt-BR',
|
|
4
|
+
currency = 'BRL',
|
|
5
|
+
): string => {
|
|
6
|
+
return new Intl.NumberFormat(language, {
|
|
7
|
+
style: 'currency',
|
|
8
|
+
currency,
|
|
9
|
+
minimumFractionDigits: 2,
|
|
10
|
+
}).format(number)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const formatDateAndWeekday = (date: Date | string, language = 'pt-BR'): string =>
|
|
14
|
+
new Intl.DateTimeFormat(language, {
|
|
15
|
+
day: '2-digit',
|
|
16
|
+
month: 'long',
|
|
17
|
+
weekday: 'long',
|
|
18
|
+
}).format(new Date(date))
|
|
19
|
+
|
|
20
|
+
export const formatDateAndWeekdayAndYear = (
|
|
21
|
+
date: Date | string,
|
|
22
|
+
language = 'pt-BR',
|
|
23
|
+
): string =>
|
|
24
|
+
new Intl.DateTimeFormat(language, {
|
|
25
|
+
day: '2-digit',
|
|
26
|
+
month: 'long',
|
|
27
|
+
weekday: 'long',
|
|
28
|
+
year: 'numeric',
|
|
29
|
+
}).format(new Date(date))
|
|
30
|
+
|
|
31
|
+
export const formatDateAndMonth = (date: Date | string, language = 'pt-BR'): string =>
|
|
32
|
+
new Intl.DateTimeFormat(language, {
|
|
33
|
+
day: '2-digit',
|
|
34
|
+
month: 'long',
|
|
35
|
+
}).format(new Date(date))
|
|
36
|
+
|
|
37
|
+
export const formatLongDate = (date: Date | string, language = 'pt-BR'): string =>
|
|
38
|
+
new Intl.DateTimeFormat(language, { dateStyle: 'long' }).format(
|
|
39
|
+
typeof date === 'string' ? new Date(date) : date,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export const formatShortDate = (date: Date, language = 'pt-BR'): string =>
|
|
43
|
+
new Intl.DateTimeFormat(language, { dateStyle: 'short' }).format(date)
|
|
44
|
+
|
|
45
|
+
export const getMonthYearFromISODate = (date: string): string => {
|
|
46
|
+
return date.slice(0, 7)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const formatWeekDayAndShortDate = (date: Date, language = 'pt-BR') => {
|
|
50
|
+
return date.toLocaleDateString(language, {
|
|
51
|
+
weekday: 'short',
|
|
52
|
+
day: '2-digit',
|
|
53
|
+
month: '2-digit',
|
|
54
|
+
})
|
|
55
|
+
}
|