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.
@@ -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';
@@ -5,6 +5,7 @@ export * from './badge';
5
5
  export * from './button';
6
6
  export * from './card';
7
7
  export * from './checkbox';
8
+ export * from './currency-input';
8
9
  export * from './dropdown-menu';
9
10
  export * from './input';
10
11
  export * from './password-input';
@@ -1 +1,2 @@
1
1
  export * from './utils/cn';
2
+ export * from './utils/formatters';
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildgrid-ui",
3
- "version": "1.1.0-dev.7",
3
+ "version": "1.1.0-dev.8",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -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'
@@ -5,6 +5,7 @@ export * from './badge'
5
5
  export * from './button'
6
6
  export * from './card'
7
7
  export * from './checkbox'
8
+ export * from './currency-input'
8
9
  export * from './dropdown-menu'
9
10
  export * from './input'
10
11
  export * from './password-input'
package/src/lib/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './utils/cn'
2
+ export * from './utils/formatters'
@@ -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
+ }