diff --git a/package.json b/package.json index f4d2402..0f2ff2c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@squidit/react-css", - "version": "0.0.11", + "version": "0.0.12", "scripts": { "format": "prettier --write --parser typescript '**/*.{ts,tsx}'", "lint": "eslint src --ext js,ts,tsx", diff --git a/src/assets/locales/en.json b/src/assets/locales/en.json index e4c9e94..876ab3d 100644 --- a/src/assets/locales/en.json +++ b/src/assets/locales/en.json @@ -1,3 +1,67 @@ { - "required": "Required field" -} \ No newline at end of file + "back": "back", + "loading": "Loading", + "required": "Required field", + "emptyField": "Fill this field", + "showPassword": "Show password", + "hidenPassword": "Hide password", + "nameSpecialChars": "Name cannot contain special characters", + "invalidName": "Invalid name", + "invalidEmail": "Invalid Email", + "invalidPhone": "Invalid Phone", + "invalidDate": "Invalid Date", + "invalidUrl": "Invalid URL", + "unexpected": "Everyone makes mistakes
This time it was our servers, but we're already fixing it! Try again later.", + "selectedFiles": "Selected Files", + "fileSize": "File too large", + "resolution": "Recommended size: {{width}}px by {{height}}px", + "mediaLabel": "Add Media", + "fields": { + "password": { + "isEmpty": "Required field", + "lessThanEight": "Minimum 8 characters", + "noNumber": "Minimum 1 number", + "noUppercase": "Uppercase", + "noLowercase": "Lowercase" + }, + "multiSelect": { + "maxOptionsSelected_one": "Maximum {{ count }} option", + "maxOptionsSelected_other": "Maximum {{ count }} options", + "qtdTags_one": "{{ count }} selected option", + "qtdTags_other": "{{ count }} selected options" + } + }, + "search": "Search", + "placeholderState": "Fill in with state", + "endScroll": "No more data available", + "year_other": "{{ value }} years ago", + "year_one": "{{ value }} year ago", + "month_other": "{{ value }} months ago", + "month_one": "{{ value }} month ago", + "day_other": "{{ value }} days ago", + "day_one": "{{ value }} day ago", + "hour_other": "{{ value }} hours ago", + "hour_one": "{{ value }} hour ago", + "today": "Today", + "biggerThanMaxDate": "Date cannot be greater than {{ maxDate }}", + "errors": { + "default": "
Everybody gets it wrong
This time it was our servers, but we're already fixing it! Please try again later.
", + "login": { + "contract_refused": "
Contract not accepted
Accept the contract to access the platform !
", + "mail_verified": "
Unverified email
You have not yet confirmed your email address , go to your inbox and click 'Verify email'! If you have not yet received the confirmation message, CLICK HERE
", + "unregistered_user": "
User not found
You entered an email that could not be found on our platform. If you have not yet registered, Click here to register.
", + "unregistered_user_mq": "
User not found on this platform
You entered an email that is not is linked to this platform. If you have not yet registered, Click here to register.
", + "too_many_attempts": "
We detected suspicious activity!
This account is blocked due to the number of attempts. Try it again later.
", + "access_denied": "
Invalid password
You entered the wrong password, please try again or in ' I forgot my password' to reset your password.
", + "unexpected": "
Everyone makes mistakes
This time it was our servers, but we are already giving a way! Please try again later.
", + "unauthorized": "
Unauthorized User
Your user is not authorized to login to the platform, contact the support.
", + "not_allowed": "
Unauthorized User
You do not have permission to access the platform.
", + "resendEmail": "
Confirmation email sent!
Now access your e-mail and click on 'Verify E-mail' to be able to browse our portal!
" + }, + "payments": { + "bankDocumentNotEqual": "The documents must be the same as the registration.", + "bankSaveError": "There was a problem saving your data. Try again later", + "blockedFields": "Temporarily this data cannot be edited." + } + } +} diff --git a/src/assets/locales/es.json b/src/assets/locales/es.json index 0574389..3d3e428 100644 --- a/src/assets/locales/es.json +++ b/src/assets/locales/es.json @@ -1,3 +1,67 @@ { - "required": "Campo requerido" -} \ No newline at end of file + "back": "devolver", + "loading": "Cargando", + "required": "Campo requerido", + "emptyField": "Rellena este campo", + "showPassword": "Mostrar contraseña", + "hidenPassword": "Ocultar contraseña", + "nameSpecialChars": "El nombre no puede contener caracteres especiales", + "invalidName": "Nombre inválido", + "invalidEmail": "Correo electrónico no válido", + "invalidPhone": "Teléfono inválido", + "invalidDate": "Fecha Invalida", + "invalidUrl": "URL no válida", + "unexpected": "Todo el mundo comete errores
Esta vez fueron nuestros servidores, ¡pero ya lo estamos arreglando! Vuelve a intentarlo más tarde.", + "selectedFiles": "Archivos seleccionados", + "fileSize": "Archivo demasiado grande", + "resolution": "Tamaño recomendado: {{width}}px por {{height}}px", + "mediaLabel": "Agregar medios", + "fields": { + "password": { + "isEmpty": "Campo obligatorio", + "lessThanEight": "Mínimo 8 caracteres", + "noNumber": "Mínimo 1 número", + "noUppercase": "Mayúsculas", + "noLowercase": "En minúsculas" + }, + "multiSelect": { + "maxOptionsSelected_one": "Opción {{ count }} máxima", + "maxOptionsSelected_other": "{{ count }} opciones máximas", + "qtdTags_one": "{{ count }} opción seleccionada", + "qtdTags_other": "{{ count }} opciones seleccionadas" + } + }, + "search": "Búsqueda", + "placeholderState": "Rellenar con el estado", + "endScroll": "No hay más datos disponibles", + "year_other": "hace {{ value }} años", + "year_one": "hace {{ value }} años", + "month_other": "hace {{ value }} meses", + "month_one": "hace {{ value }} meses", + "day_other": "hace {{ value }} días", + "day_one": "hace {{ value }} días", + "hour_other": "hace {{ value }} horas", + "hour_one": "hace {{ value }} horas", + "today": "Este Dia", + "biggerThanMaxDate": "La fecha no puede ser mayor que {{ maxDate }}", + "errors": { + "default": "
Todos cometemos errores
Esta vez fueron nuestros servidores, pero nosotros ya está dando un paso! Vuelva a intentarlo más tarde.
", + "login": { + "contract_refused": "
Contrato no aceptado
¡Acepta el contrato para acceder a la plataforma!
", + "mail_verified": "
Correo electrónico no verificado
Aún no ha confirmado su dirección de correo electrónico, vaya a su bandeja de entrada y haga clic en 'Verificar correo electrónico'. Si aún no ha recibido el mensaje de confirmación, HAGA CLIC AQUÍ
", + "unregistered_user": "
Usuario no encontrado
Ingresó un correo electrónico que no se pudo encontrar en nuestra plataforma. Si aún no se ha registrado, Haga clic aquí para registrarse.
", + "unregistered_user_mq": "
Usuario no encontrado en esta plataforma
Ingresó un correo electrónico que no es está vinculado a esta plataforma. Si aún no se ha registrado, Haga clic aquí para registrarse.
", + "too_many_attempts": "
¡Detectamos actividad sospechosa!
Esta cuenta está bloqueada debido a la cantidad de intentos . Vuelve a intentarlo más tarde.
", + "access_denied": "
Contraseña no válida
Ingresó una contraseña incorrecta, intente nuevamente o en 'Olvidé mi contraseña' para restablecer tu contraseña.
", + "unexpected": "
Todos cometemos errores
Esta vez fueron nuestros servidores, pero nosotros ya está dando un paso! Vuelva a intentarlo más tarde.
", + "unauthorized": "
Usuario no autorizado
Su usuario no está autorizado para iniciar sesión en la plataforma, comuníquese con el apoyo.
", + "not_allowed": "
Usuario no autorizado
No tienes permiso para acceder a la plataforma.
", + "resendEmail": "
¡Correo electrónico de confirmación enviado!
Ahora acceda a su correo electrónico y haga clic en 'Verificar E-mail' para poder navegar por nuestro portal!
" + }, + "payments": { + "bankDocumentNotEqual": "Los documentos deben ser los mismos que el registro.", + "bankSaveError": "Hubo un problema al guardar tus datos. Vuelve a intentarlo más tarde!", + "blockedFields": "Temporalmente estos datos no se pueden editar." + } + } +} diff --git a/src/assets/locales/pt.json b/src/assets/locales/pt.json index 027268b..bb81066 100644 --- a/src/assets/locales/pt.json +++ b/src/assets/locales/pt.json @@ -1,3 +1,66 @@ { - "required": "Campo obrigatório" -} \ No newline at end of file + "back": "voltar", + "loading": "Carregando", + "required": "Campo obrigatório", + "emptyField": "Preencha esse campo", + "showPassword": "Mostrar senha", + "hidenPassword": "Esconder senha", + "nameSpecialChars": "Nome não pode conter caracteres especiais", + "invalidName": "Nome inválido", + "invalidEmail": "E-mail inválido", + "invalidPhone": "Telefone inválido", + "invalidDate": "Data inválida", + "invalidUrl": "URL inválida", + "unexpected": "Todo mundo comete erros
Desta vez foram nossos servidores, mas já estamos corrigindo! Tente novamente mais tarde.", + "selectedFiles": "Arquivo(s) selecionado(s)", + "fileSize": "Arquivo muito grande", + "resolution": "Tamanho recomendado: {{width}}px por {{height}}px", + "mediaLabel": "Adicionar Mídia", + "fields": { + "password": { + "lessThanEight": "Mínimo de 8 caracteres", + "noNumber": "Mínimo de 1 número", + "noUppercase": "Letra maiúscula", + "noLowercase": "Letra minúscula" + }, + "multiSelect": { + "maxOptionsSelected_one": "No máximo {{ count }} opção", + "maxOptionsSelected_other": "No máximo {{ count }} opções", + "qtdTags_one": "{{ count }} opção selecionada", + "qtdTags_other": "{{ count }} opções selecionadas" + } + }, + "search": "Buscar", + "placeholderState": "Preencha com o estado", + "endScroll": "Não há mais dados disponíveis", + "year_other": "há {{ value }} anos", + "year_one": "há {{ value }} ano", + "month_other": "há {{ value }} meses", + "month_one": "há {{ value }} mês", + "day_other": "há {{ value }} dias", + "day_one": "há {{ value }} dia", + "hour_other": "há {{ value }} horas", + "hour_one": "há {{ value }} hora", + "today": "Hoje", + "biggerThanMaxDate": "Data não pode ser maior que {{ maxDate }}", + "errors": { + "default": "

Todo mundo erra. Desta vez foram nossos servidores, mas já estamos dando um jeito! Tente novamente mais tarde.

", + "login": { + "contract_refused": "
Contrato não aceito
Aceite o contrato para poder acessar a plataforma!
", + "mail_verified": "
E-mail não confirmado
Você ainda não confirmou seu endereço de e-mail, acesse sua caixa de entrada e clique em 'Verificar e-mail'! Se ainda não recebeu a mensagem de confirmação, CLIQUE AQUI
", + "unregistered_user": "
Usuário não encontrado
Você digitou um e-mail que não foi encontrado na nossa plataforma. Se ainda não tem cadastro, Clique aqui para realizar o cadastro.
", + "unregistered_user_mq": "
Usuário não encontrado nesta plataforma
Você digitou um e-mail que não está vinculado a esta plataforma. Se ainda não tem cadastro, Clique aqui para realizar o cadastro.
", + "too_many_attempts": "
Detectamos atividade suspeita!
Esta conta está bloqueada devido ao numero de tentativas. Tente novamente mais tarde.
", + "access_denied": "
Senha inválida
Você digitou a senha incorreta, tente novamente ou em 'Esqueci minha senha' para redefinir a sua senha.
", + "unexpected": "
Todo mundo erra
Desta vez foram nossos servidores, mas já estamos dando um jeito! Tente novamente mais tarde.
", + "unauthorized": "
Usuário não autorizado
Seu usuário não está autorizado a logar na plataforma, contate o suporte.
", + "not_allowed": "
Usuário não autorizado
Você não tem permissão para acessar a plataforma.
", + "resendEmail": "
E-mail de confirmação enviado!
Agora acesse a caixa de entrada do seu e-mail e clique em 'Verificar E-mail' para poder navegar em nosso portal!
" + }, + "payments": { + "bankDocumentNotEqual": "Os documentos CPF do titular deve ser igual ao do cadastro.", + "bankSaveError": "Ocorreu um problema ao salvar seus dados. Tente novamente mais tarde!", + "blockedFields": "Temporariamente esses dados não podem ser editados." + } + } +} diff --git a/src/components/index.ts b/src/components/index.ts index b1d45a6..423b324 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -9,6 +9,8 @@ export * from './sq-tab' export * from './sq-tabs' export * from './sq-infinity-scroll' export * from './inputs/sq-input' +export * from './inputs/sq-input-date' +export * from './inputs/sq-input-file' export * from './sq-banner' export * from './sq-bar-chart' export * from './sq-metric-chart' diff --git a/src/components/inputs/sq-input-date/__docs__/sq-input-date.component.example.tsx b/src/components/inputs/sq-input-date/__docs__/sq-input-date.component.example.tsx new file mode 100644 index 0000000..d33e065 --- /dev/null +++ b/src/components/inputs/sq-input-date/__docs__/sq-input-date.component.example.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { Props } from '../../sq-input/sq-input.component' +import SqInputDateComponent, { DateProps } from '../sq-input-date.component' + +const SqInputDateExample = ({ ...props }: Props & DateProps) => { + return ( +
+ +
+ ) +} + +export default SqInputDateExample diff --git a/src/components/inputs/sq-input-date/__docs__/sq-input-date.component.stories.tsx b/src/components/inputs/sq-input-date/__docs__/sq-input-date.component.stories.tsx new file mode 100644 index 0000000..ea2dfb8 --- /dev/null +++ b/src/components/inputs/sq-input-date/__docs__/sq-input-date.component.stories.tsx @@ -0,0 +1,13 @@ +import { Meta, StoryObj } from '@storybook/react' +import SqInputDateExample from './sq-input-date.component.example' + +const meta: Meta = { + title: 'Components/Inputs/SqInputDate', + component: SqInputDateExample, + tags: ['autodocs'], +} + +export default meta +type Story = StoryObj + +export const Default: Story = {} diff --git a/src/components/inputs/sq-input-date/index.ts b/src/components/inputs/sq-input-date/index.ts new file mode 100644 index 0000000..31faa65 --- /dev/null +++ b/src/components/inputs/sq-input-date/index.ts @@ -0,0 +1 @@ +export { default as SqInputDate } from './sq-input-date.component' diff --git a/src/components/inputs/sq-input-date/sq-input-date.component.scoped.scss b/src/components/inputs/sq-input-date/sq-input-date.component.scoped.scss new file mode 100644 index 0000000..433c7da --- /dev/null +++ b/src/components/inputs/sq-input-date/sq-input-date.component.scoped.scss @@ -0,0 +1,18 @@ +input::-webkit-calendar-picker-indicator { + background-color: var(--gray_light); + border-radius: 50%; + padding: 3px; + cursor: pointer; + width: 15px; + height: 15px; + transition: all 0.3s ease; + // border: 1px solid var(--black); + color: var(--black); + &:hover { + background-color: var(--white); + } +} + +span.message-error { + font-size: 0.9rem; +} diff --git a/src/components/inputs/sq-input-date/sq-input-date.component.tsx b/src/components/inputs/sq-input-date/sq-input-date.component.tsx new file mode 100644 index 0000000..c8bf1ee --- /dev/null +++ b/src/components/inputs/sq-input-date/sq-input-date.component.tsx @@ -0,0 +1,191 @@ +import { Props, State } from '@components/inputs/sq-input/sq-input.component' +import { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import SqValidatorHelper from '@helpers/sq-validator/sq-validator.helper' + +import '@components/inputs/sq-input/sq-input.component.scoped.scss' +import './sq-input-date.component.scoped.scss' + +const DELAY_TIME_FOR_TYPING_PAUSE = 800 + +interface DateState extends State { + dateObject: any + type: 'date' | 'month' +} + +export interface DateProps extends Props { + type: 'date' | 'month' | 'text' + maxDate?: string +} + +export default ({ + label, + disabled = false, + readOnly = false, + requiredOnLoad = false, + errorSpan = true, + required, + externalError, + errorIcon = , + id, + name, + type = 'date', + leftLabel, + rightLabel, + value = '', + placeholder, + backgroundColor = '', + autoComplete = 'off', + className = '', + maxDate = '9999-12-31', + onChange, + onValidate, + onTimeout, +}: DateProps) => { + const timestamp = `random-id-${(1 + Date.now() + Math.random()).toString().replace('.', '')}` + const validatorHelper = new SqValidatorHelper() + const [timer, setTimer] = useState(null) + const [state, setState] = useState({ + value: value, + error: '', + dateObject: '', + type: 'date', + }) + const { t, i18n } = useTranslation() + + function parseDate(str: string) { + const m = str?.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/) + return m ? `${m[3]}-${m[2]}-${m[1]}` : str + } + + const formatDatesByType = useCallback(() => { + const newState = { ...state } + + // eslint-disable-next-line + value = parseDate(value?.split('T')[0] || value) + + newState.dateObject = new Date(value as string) + if (type === 'month') { + newState.value = formatMonth(value as string) + } + if (type === 'date') { + newState.value = formatDate(value as string) + } + newState.type = type === 'text' ? 'date' : type + handleChange(newState.value) + }, [state, value, type]) + + const formatMonth = (date: string) => { + return `${date.split('-')[0]}-` + `${date.split('-')[1]}` + } + + const formatDate = (date: string) => { + try { + return new Date(date?.replace(/\//g, '-')).toISOString()?.split('T')[0] + } catch (e) { + return '' + } + } + + const handleChange = (event: any) => { + state.value = event?.target?.value || '' + if (state.type === 'date') { + state.dateObject = event?.target?.valueAsDate + } + if (state.type === 'month') { + state.dateObject = new Date(state.value) + } + + if (externalError) { + state.error = '' + } else if (!!required && (!state.value || state.value.length < 1)) { + state.error = t('required') + } else if (!validatorHelper.date(state.value)) { + state.error = t('invalidDate') + } else if (maxDate && new Date(state.value) > new Date(maxDate)) { + state.error = t('biggerThanMaxDate', { maxDate: new Date(maxDate).toLocaleDateString(i18n?.language) }) + } else { + state.error = '' + } + setState({ ...state }) + if (onValidate && typeof onValidate === 'function') { + onValidate(!state.error && !externalError) + } + if (onChange && typeof onChange === 'function') { + onChange(state.value) + } + + if (onTimeout && typeof onTimeout === 'function') { + onTimeout(false) + if (timer) { + clearTimeout(timer) + } + setTimer( + setTimeout(() => { + onTimeout(true) + }, DELAY_TIME_FOR_TYPING_PAUSE), + ) + } + } + + useEffect(() => { + if (state.value !== value || value.includes('T')) { + formatDatesByType() + } + }, [value, formatDatesByType, state]) + + useEffect(() => { + return () => { + clearTimeout(timer) + } + }, [timer]) + + return ( +
+ {label ? ( + + ) : null} +
+ {leftLabel ? {leftLabel} : null} + + {rightLabel ? {rightLabel} : null} +
+ {errorSpan ? ( +
+ {requiredOnLoad && !state.value && !state.error && !externalError ? ( +
+ + {t('emptyField')} +
+ ) : null} + {state.error || externalError ? errorIcon || : null} + {externalError ? externalError : ''} + {state.error && !externalError ? state.error : ''} +
+ ) : null} +
+ ) +} diff --git a/src/components/inputs/sq-input-file/__docs__/sq-input-file.component.stories.tsx b/src/components/inputs/sq-input-file/__docs__/sq-input-file.component.stories.tsx index fccd2ce..ac3e47c 100644 --- a/src/components/inputs/sq-input-file/__docs__/sq-input-file.component.stories.tsx +++ b/src/components/inputs/sq-input-file/__docs__/sq-input-file.component.stories.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Meta, StoryObj } from '@storybook/react' import SqInputFileExample from './sq-input-file.component.example' diff --git a/src/components/inputs/sq-input-file/sq-input-file.component.tsx b/src/components/inputs/sq-input-file/sq-input-file.component.tsx index be6fd7e..c9562c6 100644 --- a/src/components/inputs/sq-input-file/sq-input-file.component.tsx +++ b/src/components/inputs/sq-input-file/sq-input-file.component.tsx @@ -1,7 +1,8 @@ import Loader from '@components/sq-loader/sq-loader.component' -import ColorsHelper from '@/helpers/sq-colors/sq-colors.helper' -import ObjectHelper from '@/helpers/sq-object/sq-object.helper' +import ColorsHelper from '@helpers/sq-colors/sq-colors.helper' +import ObjectHelper from '@helpers/sq-object/sq-object.helper' import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import './sq-input-file.component.scoped.scss' @@ -77,6 +78,7 @@ export default ({ error: '', }) const [buttonHover, setButtonHover] = useState(false) + const { t } = useTranslation() const getHover = (color: string) => { return colorsHelper?.lightenDarkenColor(colorsHelper?.getCssVariableValue(color), -50) @@ -189,7 +191,7 @@ export default ({ {requiredOnLoad && !state.value && !state.error && !externalError ? (
- Fill this field + {t('emptyField')}
) : null} {state.error || externalError ? errorIcon || : null} @@ -201,7 +203,7 @@ export default ({
{state?.value?.length > 0 ? ( <> - Selected Files: + {t('selectedFiles')}:
    {Array.from(state?.value)?.map((file: any, index) =>
  • {file?.name}
  • )}
) : null} diff --git a/src/components/inputs/sq-input/sq-input.component.tsx b/src/components/inputs/sq-input/sq-input.component.tsx index 8fde175..4e66cef 100644 --- a/src/components/inputs/sq-input/sq-input.component.tsx +++ b/src/components/inputs/sq-input/sq-input.component.tsx @@ -1,10 +1,10 @@ import SqLoader from '@components/sq-loader/sq-loader.component' -import SqValidatorHelper from '@/helpers/sq-validator/sq-validator.helper' +import SqValidatorHelper from '@helpers/sq-validator/sq-validator.helper' import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import './sq-input.component.scoped.scss' import './sq-input.component.scss' -import { useTranslation } from 'react-i18next' const DELAY_TIME_FOR_TYPING_PAUSE = 800 diff --git a/src/components/sq-banner/banner.component.test.tsx b/src/components/sq-banner/banner.component.test.tsx index fd1c5f0..0b26c65 100644 --- a/src/components/sq-banner/banner.component.test.tsx +++ b/src/components/sq-banner/banner.component.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest' import { render, screen } from '@testing-library/react' -import Banner from '@/components/sq-banner/banner.component' +import Banner from '@components/sq-banner/banner.component' describe('Banner Component', () => { const bannerComponent = ( diff --git a/src/components/sq-bar-chart/sq-bar-chart.component.tsx b/src/components/sq-bar-chart/sq-bar-chart.component.tsx index 72fd667..d05ae13 100644 --- a/src/components/sq-bar-chart/sq-bar-chart.component.tsx +++ b/src/components/sq-bar-chart/sq-bar-chart.component.tsx @@ -1,6 +1,6 @@ import { useLayoutEffect, useMemo, useRef } from 'react' -import SqNumbersHelper from '@/helpers/sq-numbers/sq-numbers.helper' -import useRect from '@/hooks/use-sq-rect/use-sq-rect.hook' +import SqNumbersHelper from '@helpers/sq-numbers/sq-numbers.helper' +import useRect from '@hooks/use-sq-rect/use-sq-rect.hook' import './sq-bar-chart.component.scoped.scss' diff --git a/src/components/sq-button/sq-button.component.tsx b/src/components/sq-button/sq-button.component.tsx index 8aa4b08..d391370 100644 --- a/src/components/sq-button/sq-button.component.tsx +++ b/src/components/sq-button/sq-button.component.tsx @@ -1,5 +1,5 @@ import React, { PropsWithChildren, useState } from 'react' -import SqColorsHelper from '@/helpers/sq-colors/sq-colors.helper' +import SqColorsHelper from '@helpers/sq-colors/sq-colors.helper' import { SqLoader } from '@components/sq-loader' import './sq-button.component.scoped.scss' diff --git a/src/components/sq-metric-chart/__docs__/sq-metric-chart.component.example.tsx b/src/components/sq-metric-chart/__docs__/sq-metric-chart.component.example.tsx index 31b2a99..a9c92c4 100644 --- a/src/components/sq-metric-chart/__docs__/sq-metric-chart.component.example.tsx +++ b/src/components/sq-metric-chart/__docs__/sq-metric-chart.component.example.tsx @@ -1,8 +1,8 @@ import React from 'react' -import SqMetricChartExample, { Props } from '../sq-metric-chart.component' +import SqMetricChartExample from '../sq-metric-chart.component' import { MetricChartProvider } from '../../../hooks/use-sq-metric-chart-context/use-metric-chart-context.hook' -const SqMetricChart = ({ ...props }: Props) => { +const SqMetricChart = ({ ...props }) => { return ( { {metric?.first && ( - #1 - {percentage ? formatPercent(metric?.first) : formatCompactNumber({ lang: 'en', number: metric?.first })} + #1 -{' '} + + {percentage ? formatPercent(metric?.first) : formatCompactNumber({ lang: i18n?.language, number: metric?.first })} + )}
diff --git a/src/components/sq-metric-chart/components/metric-chart-body/metric-chart-body.component.tsx b/src/components/sq-metric-chart/components/metric-chart-body/metric-chart-body.component.tsx index b24f7b6..5fd57a7 100644 --- a/src/components/sq-metric-chart/components/metric-chart-body/metric-chart-body.component.tsx +++ b/src/components/sq-metric-chart/components/metric-chart-body/metric-chart-body.component.tsx @@ -1,14 +1,12 @@ import { useLayoutEffect, useMemo, useRef } from 'react' -import SqNumbersHelper from '@/helpers/sq-numbers/sq-numbers.helper' -import { useSqMetricChartContext, useSqRect } from '@/hooks' - -interface Props { - labelAverage: string -} +import SqNumbersHelper from '@helpers/sq-numbers/sq-numbers.helper' +import { useSqMetricChartContext } from '@hooks/use-sq-metric-chart-context' +import { useSqRect } from '@hooks/use-sq-rect' import './metric-chart-body.component.scss' +import { useTranslation } from 'react-i18next' -const MetricChartBody = ({ labelAverage }: Props) => { +const MetricChartBody = () => { const numbersHelper = useMemo(() => new SqNumbersHelper(), []) const { formatCompactNumber, formatPercent } = numbersHelper const refDescription = useRef(null) @@ -19,6 +17,8 @@ const MetricChartBody = ({ labelAverage }: Props) => { const contentRectDescription = useSqRect(refDescription) const contentRectMetricChartBody = useSqRect(refMetricChartBody) + const [t] = useTranslation('metricChart') + const definePercentage = (value: number, maxValue: number): string => { return ((value * 100) / maxValue).toFixed(0) } @@ -44,7 +44,7 @@ const MetricChartBody = ({ labelAverage }: Props) => {
- {labelAverage}{' '} + {t('average')}{' '} {percentage ? formatPercent(metric.influencersAverage) diff --git a/src/components/sq-metric-chart/components/user-position/user-position.component.tsx b/src/components/sq-metric-chart/components/user-position/user-position.component.tsx index 1e485a1..8171d37 100644 --- a/src/components/sq-metric-chart/components/user-position/user-position.component.tsx +++ b/src/components/sq-metric-chart/components/user-position/user-position.component.tsx @@ -1,4 +1,4 @@ -import { useSqMetricChartContext } from '@/hooks' +import { useSqMetricChartContext } from '@hooks/use-sq-metric-chart-context' import './user-position.component.scoped.scss' diff --git a/src/components/sq-metric-chart/sq-metric-chart.component.tsx b/src/components/sq-metric-chart/sq-metric-chart.component.tsx index f83dd5a..e28e2b4 100644 --- a/src/components/sq-metric-chart/sq-metric-chart.component.tsx +++ b/src/components/sq-metric-chart/sq-metric-chart.component.tsx @@ -1,16 +1,14 @@ import MetricChartHeader from './components/header-graphic/header-graphic.component' import SqBarChart from '../sq-bar-chart/sq-bar-chart.component' -import { useSqMetricChartContext } from '@/hooks' +import { useSqMetricChartContext } from '@hooks/use-sq-metric-chart-context' import './sq-metric-chart.component.scoped.scss' +import { useTranslation } from 'react-i18next' -export interface Props { - labelPin?: string -} - -const MetricChart = ({ labelPin }: Props) => { +const MetricChart = () => { const { state } = useSqMetricChartContext() const { metric } = state + const { t } = useTranslation('metricChart') return (
@@ -19,7 +17,7 @@ const MetricChart = ({ labelPin }: Props) => { pinValue={metric?.influencersAverage} percentage={state?.percentage ?? false} total={metric?.first} - labelPin={labelPin} + labelPin={t('average')} />
) diff --git a/src/hooks/use-sq-metric-chart-context/use-metric-chart-context.hook.tsx b/src/hooks/use-sq-metric-chart-context/use-metric-chart-context.hook.tsx index 5ce6d44..f83472e 100644 --- a/src/hooks/use-sq-metric-chart-context/use-metric-chart-context.hook.tsx +++ b/src/hooks/use-sq-metric-chart-context/use-metric-chart-context.hook.tsx @@ -1,5 +1,5 @@ import { createContext, useContext } from 'react' -import { MetricsDetails } from '@/interfaces' +import { MetricsDetails } from '@interfaces/sq-metrics.interface' export interface MetricChartProps { metric: MetricsDetails diff --git a/src/hooks/use-sq-throttle/use-sq-throttle.hook.stories.tsx b/src/hooks/use-sq-throttle/use-sq-throttle.hook.stories.tsx index 81aba0e..26cce8d 100644 --- a/src/hooks/use-sq-throttle/use-sq-throttle.hook.stories.tsx +++ b/src/hooks/use-sq-throttle/use-sq-throttle.hook.stories.tsx @@ -1,6 +1,6 @@ import { useState } from 'react' import useThrottle from './use-sq-throttle.hook' -import { SqButton } from '@/components' +import { SqButton } from '@components/sq-button' export const Overview = () => { const [count, setCount] = useState(0) diff --git a/src/i18n.ts b/src/i18n.ts index 6e92120..5f44564 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -15,27 +15,50 @@ interface Translations { } } +interface AllComponentsTranslations { + [key: string]: Translations +} + import ptGlobals from '@assets/locales/pt.json' import enGlobals from '@assets/locales/en.json' import esGlobals from '@assets/locales/es.json' +import ptSqMetricChart from '@components/sq-metric-chart/locales/pt.json' +import enSqMetricChart from '@components/sq-metric-chart/locales/en.json' +import esSqMetricChart from '@components/sq-metric-chart/locales/es.json' + +const componentsThatUseGlobals = ['sqInput'] const getResources = () => ({ en: { globals: enGlobals, + sqMetricChart: enSqMetricChart, }, pt: { globals: ptGlobals, + sqMetricChart: ptSqMetricChart, }, es: { globals: esGlobals, + sqMetricChart: esSqMetricChart, }, }) export const defaultNS = 'globals' export let resources: Translations = getResources() +export const setAllComponentsTranslations = (allComponentsTranslations: AllComponentsTranslations[]) => { + if (allComponentsTranslations?.length) { + allComponentsTranslations.forEach((componentTranslations) => { + Object.keys(componentTranslations).forEach((componentName) => { + setComponentTranslations(componentName, componentTranslations[componentName]) + }) + }) + } else { + resources = getResources() + } +} + export const setComponentTranslations = (componentName: string, translations?: Translations) => { - const componentsThatUseGlobals = ['sqInput'] componentName?.[0]?.toLocaleLowerCase() if (translations) { @@ -53,7 +76,7 @@ i18n .use(initReactI18next) .init({ defaultNS, - ns: ['globals'], + ns: ['globals', 'sqMetricChart'], load: 'all', supportedLngs: ['en', 'pt', 'es'], debug: false, diff --git a/src/index.ts b/src/index.ts index ed13616..6828418 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,7 @@ export * from './helpers' export * from './hooks' export * from './interfaces' export * from './observables' -export { setComponentTranslations } from './i18n' +export { setComponentTranslations, setAllComponentsTranslations } from './i18n' onLangChange.subscribe((lang) => { i18n.changeLanguage(lang) diff --git a/tsconfig.json b/tsconfig.json index 42cce7b..64f6c60 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,12 +7,13 @@ "skipLibCheck": true, // Skips type checking of declaration files. "baseUrl": ".", "paths": { - "@/*": ["./src/*"], + "@/*": ["./*"], + "@src/*": ["./src/*"], "@assets/*": ["./src/assets/*"], "@components/*": ["./src/components/*"], "@helpers/*": ["./src/helpers/*"], "@hooks/*": ["./src/hooks/*"], - "@interfaces/*": ["./src/interfaces/*"], + "@interfaces/*": ["./src/interfaces/*"] }, "esModuleInterop": true, "allowJs": true, diff --git a/vite.config.ts b/vite.config.ts index de9cf9e..8242ab1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -27,8 +27,8 @@ export default defineConfig({ }, resolve: { alias: { - // src: '/src', - '@': path.resolve(__dirname, './src'), + '@': path.resolve(__dirname, './'), + '@src': path.resolve(__dirname, './src'), '@assets': path.resolve(__dirname, './src/assets'), '@components': path.resolve(__dirname, './src/components'), '@helpers': path.resolve(__dirname, './src/helpers'),