@axzydev/axzy_ui_system 1.4.8 → 1.5.1

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.
Files changed (223) hide show
  1. package/README.md +2 -11
  2. package/dist/index.cjs +1826 -1351
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.css +1 -1
  5. package/dist/index.css.map +1 -1
  6. package/dist/index.d.cts +115 -14
  7. package/dist/index.d.ts +115 -14
  8. package/dist/index.js +1481 -1010
  9. package/dist/index.js.map +1 -1
  10. package/dist/layered.css +1 -1
  11. package/package.json +3 -16
  12. package/src/App.tsx +90 -73
  13. package/src/components/atoms/badget/badget.tsx +23 -5
  14. package/src/components/atoms/chip/chip.tsx +3 -1
  15. package/src/components/atoms/image/image.tsx +3 -1
  16. package/src/components/atoms/input/input.tsx +8 -5
  17. package/src/components/atoms/loader/loader.tsx +3 -1
  18. package/src/components/atoms/slide/slide.props.ts +12 -0
  19. package/src/components/atoms/slide/slide.tsx +3 -0
  20. package/src/components/molecules/alert/alert.tsx +3 -1
  21. package/src/components/molecules/breadcrumbs/breadcrumbs.tsx +3 -1
  22. package/src/components/molecules/calendar/calendar.tsx +9 -6
  23. package/src/components/molecules/card/card.tsx +5 -2
  24. package/src/components/molecules/chip-input/chip-input.tsx +9 -7
  25. package/src/components/molecules/date-picker/datePicker.tsx +9 -9
  26. package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +1 -1
  27. package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +6 -3
  28. package/src/components/molecules/field/field.tsx +3 -1
  29. package/src/components/molecules/form-header/form-header.tsx +3 -1
  30. package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +2 -2
  31. package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +20 -15
  32. package/src/components/molecules/masked-input/masked-input.tsx +3 -1
  33. package/src/components/molecules/multi-select/multi-select.tsx +12 -9
  34. package/src/components/molecules/pagination/pagination.tsx +11 -5
  35. package/src/components/molecules/search-select/search-select.tsx +11 -9
  36. package/src/components/molecules/select/select.tsx +6 -4
  37. package/src/components/molecules/stepper/stepper.tsx +6 -4
  38. package/src/components/molecules/table/table.tsx +30 -28
  39. package/src/components/molecules/time-picker/timePicker.tsx +8 -6
  40. package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
  41. package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
  42. package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
  43. package/src/components/organisms/data-table/dataTable.tsx +31 -25
  44. package/src/components/organisms/dialog/dialog.tsx +3 -0
  45. package/src/components/organisms/drawer/drawer.tsx +3 -0
  46. package/src/components/organisms/dropfile/dropfile.tsx +19 -17
  47. package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
  48. package/src/components/organisms/navbar/navbar.tsx +4 -2
  49. package/src/components/organisms/page-header/page-header.tsx +3 -1
  50. package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
  51. package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
  52. package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
  53. package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
  54. package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
  55. package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
  56. package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
  57. package/src/components/organisms/searchTable/searchTable.tsx +4 -2
  58. package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
  59. package/src/components/organisms/sidebar/sidebar.tsx +9 -4
  60. package/src/components/organisms/toast/toast.tsx +3 -1
  61. package/src/components/organisms/topbar/topbar.tsx +4 -2
  62. package/src/components/templates/page/page.props.ts +4 -4
  63. package/src/components/templates/page/page.tsx +6 -4
  64. package/src/components/theme-provider/themeProvider.tsx +28 -17
  65. package/src/dev.css +61 -2
  66. package/src/global.d.ts +10 -0
  67. package/src/i18n/context.ts +9 -0
  68. package/src/i18n/index.ts +22 -0
  69. package/src/i18n/locales/en.ts +163 -0
  70. package/src/i18n/locales/es.ts +165 -0
  71. package/src/i18n/provider.tsx +44 -0
  72. package/src/i18n/translator.ts +82 -0
  73. package/src/i18n/types.ts +54 -0
  74. package/src/i18n/useITTranslation.ts +20 -0
  75. package/src/index.css +0 -1
  76. package/src/index.ts +7 -1
  77. package/src/sandbox/LandingShowcase.tsx +224 -580
  78. package/src/sandbox/NotFoundShowcase.tsx +51 -36
  79. package/src/sandbox/TopbarSearch.tsx +6 -4
  80. package/src/sandbox/controls.tsx +180 -0
  81. package/src/sandbox/i18n.ts +1939 -0
  82. package/src/sandbox/icons.tsx +25 -0
  83. package/src/sandbox/locale.tsx +72 -0
  84. package/src/sandbox/ui.tsx +474 -0
  85. package/src/sandbox/useHashRoute.ts +46 -10
  86. package/src/showcases/DataShowcases.tsx +103 -87
  87. package/src/showcases/FeedbackShowcases.tsx +37 -30
  88. package/src/showcases/FormShowcases.tsx +303 -215
  89. package/src/showcases/GettingStartedShowcase.tsx +151 -145
  90. package/src/showcases/HelpShowcase.tsx +126 -128
  91. package/src/showcases/HomeShowcase.tsx +678 -482
  92. package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
  93. package/src/showcases/NavigationShowcases.tsx +45 -37
  94. package/src/showcases/PageShowcases.tsx +171 -152
  95. package/src/showcases/ProductoDetalle.tsx +272 -129
  96. package/src/showcases/ShowcaseLayout.tsx +256 -219
  97. package/src/showcases/SizesShowcase.tsx +123 -64
  98. package/src/showcases/StructureShowcases.tsx +203 -138
  99. package/src/showcases/portfolioUi.tsx +153 -0
  100. package/src/showcases/techIcons.ts +3 -0
  101. package/src/utils/table.utils.ts +10 -2
  102. package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
  103. package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
  104. package/src/components/atoms/badget/badget.doc.mdx +0 -46
  105. package/src/components/atoms/badget/badget.stories.tsx +0 -75
  106. package/src/components/atoms/button/button.doc.mdx +0 -59
  107. package/src/components/atoms/button/button.stories.tsx +0 -173
  108. package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
  109. package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
  110. package/src/components/atoms/chip/chip.doc.mdx +0 -47
  111. package/src/components/atoms/chip/chip.stories.tsx +0 -64
  112. package/src/components/atoms/divider/divider.doc.mdx +0 -42
  113. package/src/components/atoms/divider/divider.stories.tsx +0 -33
  114. package/src/components/atoms/flex/flex.doc.mdx +0 -59
  115. package/src/components/atoms/flex/flex.stories.tsx +0 -70
  116. package/src/components/atoms/grid/grid.doc.mdx +0 -54
  117. package/src/components/atoms/grid/grid.stories.tsx +0 -71
  118. package/src/components/atoms/image/image.doc.mdx +0 -44
  119. package/src/components/atoms/image/image.stories.tsx +0 -48
  120. package/src/components/atoms/input/input.doc.mdx +0 -70
  121. package/src/components/atoms/input/input.stories.tsx +0 -113
  122. package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
  123. package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
  124. package/src/components/atoms/loader/loader.doc.mdx +0 -43
  125. package/src/components/atoms/loader/loader.stories.tsx +0 -104
  126. package/src/components/atoms/popover/popover.doc.mdx +0 -53
  127. package/src/components/atoms/popover/popover.stories.tsx +0 -24
  128. package/src/components/atoms/progress/progress.doc.mdx +0 -48
  129. package/src/components/atoms/progress/progress.stories.tsx +0 -39
  130. package/src/components/atoms/radio/radio.doc.mdx +0 -62
  131. package/src/components/atoms/radio/radio.stories.tsx +0 -48
  132. package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
  133. package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
  134. package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
  135. package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
  136. package/src/components/atoms/slide/slide.doc.mdx +0 -46
  137. package/src/components/atoms/slide/slide.stories.tsx +0 -120
  138. package/src/components/atoms/slider/slider.doc.mdx +0 -49
  139. package/src/components/atoms/slider/slider.stories.tsx +0 -29
  140. package/src/components/atoms/stack/stack.doc.mdx +0 -126
  141. package/src/components/atoms/stack/stack.stories.tsx +0 -283
  142. package/src/components/atoms/text/text.doc.mdx +0 -41
  143. package/src/components/atoms/text/text.stories.tsx +0 -66
  144. package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
  145. package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
  146. package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
  147. package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
  148. package/src/components/isolation/isolation.stories.tsx +0 -202
  149. package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
  150. package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
  151. package/src/components/molecules/alert/alert.doc.mdx +0 -48
  152. package/src/components/molecules/alert/alert.stories.tsx +0 -40
  153. package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
  154. package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
  155. package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
  156. package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
  157. package/src/components/molecules/card/card.doc.mdx +0 -54
  158. package/src/components/molecules/card/card.stories.tsx +0 -57
  159. package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
  160. package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
  161. package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
  162. package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
  163. package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
  164. package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
  165. package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
  166. package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
  167. package/src/components/molecules/field/field.doc.mdx +0 -43
  168. package/src/components/molecules/field/field.stories.tsx +0 -42
  169. package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
  170. package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
  171. package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
  172. package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
  173. package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
  174. package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
  175. package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
  176. package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
  177. package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
  178. package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
  179. package/src/components/molecules/select/select.doc.mdx +0 -75
  180. package/src/components/molecules/select/select.stories.tsx +0 -85
  181. package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
  182. package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
  183. package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
  184. package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
  185. package/src/components/molecules/table/table.doc.mdx +0 -140
  186. package/src/components/molecules/table/table.stories.tsx +0 -402
  187. package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
  188. package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
  189. package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
  190. package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
  191. package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
  192. package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
  193. package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
  194. package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -132
  195. package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
  196. package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
  197. package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
  198. package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
  199. package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
  200. package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
  201. package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
  202. package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
  203. package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
  204. package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
  205. package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
  206. package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
  207. package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
  208. package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
  209. package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
  210. package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
  211. package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
  212. package/src/components/organisms/toast/toast.doc.mdx +0 -50
  213. package/src/components/organisms/toast/toast.stories.tsx +0 -137
  214. package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
  215. package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
  216. package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
  217. package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
  218. package/src/components/templates/layout/layout.doc.mdx +0 -53
  219. package/src/components/templates/layout/layout.stories.tsx +0 -113
  220. package/src/components/templates/page/page.doc.mdx +0 -86
  221. package/src/components/templates/page/page.stories.tsx +0 -97
  222. package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
  223. package/src/components/theme-provider/themeProvider.stories.tsx +0 -58
@@ -5,6 +5,7 @@ import { theme } from "@/theme/theme";
5
5
  import { inputFieldStyle } from "@/utils/styles";
6
6
  import ITChip from "@/components/atoms/chip/chip";
7
7
  import ITText from "@/components/atoms/text/text";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  /**
10
11
  * Free-text input that turns entered values into removable tags (chips).
@@ -24,7 +25,7 @@ import ITText from "@/components/atoms/text/text";
24
25
  export default function ITChipInput({
25
26
  name,
26
27
  label,
27
- placeholder = "Escribe y presiona Enter",
28
+ placeholder,
28
29
  value,
29
30
  onChange,
30
31
  onBlur,
@@ -41,6 +42,7 @@ export default function ITChipInput({
41
42
  helpText,
42
43
  className,
43
44
  }: ITChipInputProps) {
45
+ const { t } = useITTranslation();
44
46
  const [inputValue, setInputValue] = useState("");
45
47
  const [localTouched, setLocalTouched] = useState(false);
46
48
  const [localError, setLocalError] = useState<string | undefined>(undefined);
@@ -53,10 +55,10 @@ export default function ITChipInput({
53
55
  const externalError =
54
56
  error !== undefined && error !== false
55
57
  ? error === true
56
- ? "Este campo es requerido"
58
+ ? t("field.required")
57
59
  : error
58
60
  : required && value.length === 0
59
- ? "Este campo es requerido"
61
+ ? t("field.required")
60
62
  : undefined;
61
63
  const effectiveError = localError || externalError;
62
64
  const hasError = isTouched && !!effectiveError;
@@ -88,11 +90,11 @@ export default function ITChipInput({
88
90
  const tag = raw.trim();
89
91
  if (!tag) return;
90
92
  if (maxTags !== undefined && value.length >= maxTags) {
91
- setLocalError(`Máximo ${maxTags} etiquetas`);
93
+ setLocalError(t("chipInput.maxTags", { count: maxTags }));
92
94
  return;
93
95
  }
94
96
  if (!allowDuplicates && value.includes(tag)) {
95
- setLocalError("Etiqueta duplicada");
97
+ setLocalError(t("chipInput.duplicate"));
96
98
  return;
97
99
  }
98
100
  const validationError = validate?.(tag);
@@ -107,7 +109,7 @@ export default function ITChipInput({
107
109
 
108
110
  const removeTag = (tag: string) => {
109
111
  if (disabled) return;
110
- onChange?.(value.filter((t) => t !== tag));
112
+ onChange?.(value.filter((item) => item !== tag));
111
113
  };
112
114
 
113
115
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
@@ -188,7 +190,7 @@ export default function ITChipInput({
188
190
  onFocus={() => setIsFocused(true)}
189
191
  onBlur={handleBlur}
190
192
  disabled={disabled}
191
- placeholder={value.length === 0 ? placeholder : ""}
193
+ placeholder={value.length === 0 ? (placeholder ?? t("chipInput.placeholder")) : ""}
192
194
  autoComplete="off"
193
195
  className="flex-1 min-w-[100px] bg-transparent outline-none border-none text-sm text-secondary-900 dark:text-slate-100"
194
196
  style={{ padding: 0, fontSize: inputFieldStyle(inputTheme, size).fontSize }}
@@ -8,6 +8,7 @@ import ITInput from "@/components/atoms/input/input";
8
8
  import { ITDatePickerProps } from "./date-picker.props";
9
9
  import { useFloatingPanel } from "@/hooks/useFloatingPanel";
10
10
  import { theme } from "@/theme/theme";
11
+ import { useITTranslation } from "@/i18n";
11
12
 
12
13
  /**
13
14
  * Date picker component with single-date and date-range modes.
@@ -58,6 +59,7 @@ export default function ITDatePicker({
58
59
  maxDate,
59
60
  range = false,
60
61
  }: ITDatePickerProps) {
62
+ const { t, formatDate: formatDateLocale } = useITTranslation();
61
63
  const [isOpen, setIsOpen] = useState(false);
62
64
  const [inputValue, setInputValue] = useState("");
63
65
  const [isValidDate, setIsValidDate] = useState(true);
@@ -171,13 +173,11 @@ export default function ITDatePicker({
171
173
  };
172
174
 
173
175
  const formatDate = (date: Date) =>
174
- date
175
- .toLocaleDateString("es-ES", {
176
- day: "2-digit",
177
- month: "2-digit",
178
- year: "numeric",
179
- })
180
- .replace(/\//g, "/");
176
+ formatDateLocale(date, {
177
+ day: "2-digit",
178
+ month: "2-digit",
179
+ year: "numeric",
180
+ });
181
181
 
182
182
  const validateDate = (dateString: string) => {
183
183
  const regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
@@ -266,7 +266,7 @@ export default function ITDatePicker({
266
266
  name={name}
267
267
  type="text"
268
268
  label={label}
269
- placeholder={placeholder}
269
+ placeholder={placeholder ?? (range ? t("datePicker.rangePlaceholder") : t("datePicker.placeholder"))}
270
270
  value={inputValue}
271
271
  onChange={handleInputChange}
272
272
  onBlur={handleInputBlur}
@@ -284,7 +284,7 @@ export default function ITDatePicker({
284
284
  disabled={disabled}
285
285
  required={required}
286
286
  touched={touched}
287
- error={!isValidDate ? "Fecha inválida" : error}
287
+ error={!isValidDate ? t("datePicker.invalid") : error}
288
288
  onClick={handleIconClick}
289
289
  />
290
290
 
@@ -26,7 +26,7 @@ export interface ITDropdownMenuProps {
26
26
  items: ITDropdownMenuItem[];
27
27
  /** Custom trigger content. Defaults to a vertical ellipsis icon. */
28
28
  trigger?: ReactNode;
29
- /** Accessible label for the trigger button. @default "Abrir menú" */
29
+ /** Accessible label for the trigger button. @default i18n `dropdownMenu.open` ("Abrir menú") */
30
30
  triggerLabel?: string;
31
31
  /** Placement of the menu relative to the trigger. Valid values: `"bottom-start"`, `"bottom-end"`, `"top-start"`, `"top-end"`. @default "bottom-end" */
32
32
  placement?: "bottom-start" | "bottom-end" | "top-start" | "top-end";
@@ -5,6 +5,7 @@ import { FaEllipsisV } from "react-icons/fa";
5
5
  import { ITDropdownMenuProps } from "./dropdown-menu.props";
6
6
  import { useFloatingPanel } from "@/hooks/useFloatingPanel";
7
7
  import ITText from "@/components/atoms/text/text";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  /**
10
11
  * Action menu with keyboard navigation and ARIA menu semantics.
@@ -24,13 +25,15 @@ import ITText from "@/components/atoms/text/text";
24
25
  export default function ITDropdownMenu({
25
26
  items,
26
27
  trigger,
27
- triggerLabel = "Abrir menú",
28
+ triggerLabel,
28
29
  placement = "bottom-end",
29
30
  onSelect,
30
31
  disabled = false,
31
32
  className,
32
33
  menuClassName,
33
34
  }: ITDropdownMenuProps) {
35
+ const { t } = useITTranslation();
36
+ const resolvedTriggerLabel = triggerLabel ?? t("dropdownMenu.open");
34
37
  const [isOpen, setIsOpen] = useState(false);
35
38
  const [activeIndex, setActiveIndex] = useState(-1);
36
39
 
@@ -153,7 +156,7 @@ export default function ITDropdownMenu({
153
156
  type="button"
154
157
  aria-haspopup="menu"
155
158
  aria-expanded={isOpen}
156
- aria-label={triggerLabel}
159
+ aria-label={resolvedTriggerLabel}
157
160
  disabled={disabled}
158
161
  onClick={() => (isOpen ? close() : open())}
159
162
  onKeyDown={handleTriggerKeyDown}
@@ -174,7 +177,7 @@ export default function ITDropdownMenu({
174
177
  ref={panelRef}
175
178
  data-it-floating="true"
176
179
  role="menu"
177
- aria-label={triggerLabel}
180
+ aria-label={resolvedTriggerLabel}
178
181
  style={panelStyle}
179
182
  onKeyDown={handleMenuKeyDown}
180
183
  className={clsx(
@@ -2,6 +2,7 @@ import clsx from "clsx";
2
2
  import { ITFieldProps } from "./field.props";
3
3
  import { inputError, inputLabel } from "@/utils/styles";
4
4
  import ITText from "@/components/atoms/text/text";
5
+ import { useITTranslation } from "@/i18n";
5
6
 
6
7
  /**
7
8
  * Form-field wrapper that standardizes label, required marker, helper text,
@@ -26,8 +27,9 @@ export default function ITField({
26
27
  labelClassName,
27
28
  contentClassName,
28
29
  }: ITFieldProps) {
30
+ const { t } = useITTranslation();
29
31
  const hasError = Boolean(error);
30
- const message = typeof error === "string" ? error : hasError ? "Este campo es requerido" : undefined;
32
+ const message = typeof error === "string" ? error : hasError ? t("field.required") : undefined;
31
33
 
32
34
  return (
33
35
  <div className={clsx("flex flex-col gap-1.5", className)}>
@@ -3,6 +3,7 @@ import { ITFormHeaderProps } from "./form-header.props";
3
3
  import { useITThemeSafe } from "@/theme/theme-context";
4
4
  import { getContrastTextColor } from "@/utils/color.utils";
5
5
  import ITText from "@/components/atoms/text/text";
6
+ import { useITTranslation } from "@/i18n";
6
7
 
7
8
  /**
8
9
  * Colored header bar for modals and forms with an optional close button.
@@ -21,6 +22,7 @@ export default function ITFormHeader({
21
22
  className = "",
22
23
  }: ITFormHeaderProps) {
23
24
  const themeContext = useITThemeSafe();
25
+ const { t } = useITTranslation();
24
26
 
25
27
  const isDarkMode = themeContext?.resolvedTheme === "dark";
26
28
  const palette = themeContext?.palette;
@@ -38,7 +40,7 @@ export default function ITFormHeader({
38
40
  onClick={onClose}
39
41
  className={`absolute right-4 ${textColorClass} hover:opacity-80 transition-colors duration-200 p-1 rounded-full`}
40
42
  style={{ color: "inherit" }}
41
- aria-label="Cerrar"
43
+ aria-label={t("common.close")}
42
44
  >
43
45
  <FaTimes className="w-4 h-4" />
44
46
  </button>
@@ -11,9 +11,9 @@ export interface ITLineBarChartProps {
11
11
  data: ITLineBarDatum[];
12
12
  /** Etiqueta accesible del gráfico. */
13
13
  ariaLabel: string;
14
- /** Nombre de la serie de la línea (leyenda y tooltip). */
14
+ /** Nombre de la serie de la línea (leyenda y tooltip). @default i18n `lineBarChart.lineLabel` ("Serie A") */
15
15
  lineLabel?: string;
16
- /** Nombre de la serie de barras (leyenda y tooltip). */
16
+ /** Nombre de la serie de barras (leyenda y tooltip). @default i18n `lineBarChart.barLabel` ("Serie B") */
17
17
  barLabel?: string;
18
18
  /** Color de la línea (por defecto el verde de la casa). */
19
19
  lineColor?: string;
@@ -1,11 +1,6 @@
1
1
  import { useId, useMemo, useState } from "react";
2
2
  import type { ITLineBarChartProps } from "./line-bar-chart.props";
3
-
4
- /** Número compacto para los ejes (`104 k`). */
5
- const compact = (value: number): string =>
6
- new Intl.NumberFormat("es-MX", { notation: "compact", maximumFractionDigits: 1 }).format(value);
7
-
8
- const plain = (value: number): string => new Intl.NumberFormat("es-MX").format(value);
3
+ import { useITTranslation } from "@/i18n";
9
4
 
10
5
  /**
11
6
  * Barras + línea en SVG puro (sin librería de gráficas). Pensado para comparar
@@ -17,15 +12,25 @@ const plain = (value: number): string => new Intl.NumberFormat("es-MX").format(v
17
12
  export function ITLineBarChart({
18
13
  data,
19
14
  ariaLabel,
20
- lineLabel = "Serie A",
21
- barLabel = "Serie B",
15
+ lineLabel,
16
+ barLabel,
22
17
  lineColor = "#10b981",
23
18
  barColor = "#3b82f6",
24
19
  height = 240,
25
- formatValue = plain,
26
- formatAxis = compact,
20
+ formatValue,
21
+ formatAxis,
27
22
  className = "",
28
23
  }: ITLineBarChartProps) {
24
+ const { t, formatNumber } = useITTranslation();
25
+ const resolvedLineLabel = lineLabel ?? t("lineBarChart.lineLabel");
26
+ const resolvedBarLabel = barLabel ?? t("lineBarChart.barLabel");
27
+ /** Número compacto para los ejes (`104 k`). */
28
+ const resolvedFormatAxis =
29
+ formatAxis ??
30
+ ((value: number) =>
31
+ formatNumber(value, { notation: "compact", maximumFractionDigits: 1 }));
32
+ const resolvedFormatValue = formatValue ?? ((value: number) => formatNumber(value));
33
+
29
34
  const gradientId = useId().replace(/[:]/g, "");
30
35
  const [hover, setHover] = useState<number | null>(null);
31
36
 
@@ -93,7 +98,7 @@ export function ITLineBarChart({
93
98
  strokeDasharray={i === 0 ? undefined : "3 4"}
94
99
  />
95
100
  <text x={padding.left - 8} y={y(value) + 3} textAnchor="end" fontSize={10} fill="#94a3b8">
96
- {formatAxis(Math.round(value))}
101
+ {resolvedFormatAxis(Math.round(value))}
97
102
  </text>
98
103
  </g>
99
104
  ))}
@@ -153,10 +158,10 @@ export function ITLineBarChart({
153
158
  >
154
159
  <p className="text-[10px] font-semibold" style={{ color: "#e2e8f0" }}>{active.label}</p>
155
160
  <p className="text-[11px] tabular-nums" style={{ color: lineColor }}>
156
- {lineLabel}: {formatValue(active.line)}
161
+ {resolvedLineLabel}: {resolvedFormatValue(active.line)}
157
162
  </p>
158
163
  <p className="text-[11px] tabular-nums" style={{ color: "#93c5fd" }}>
159
- {barLabel}: {formatValue(active.bar)}
164
+ {resolvedBarLabel}: {resolvedFormatValue(active.bar)}
160
165
  </p>
161
166
  </div>
162
167
  )}
@@ -164,11 +169,11 @@ export function ITLineBarChart({
164
169
  <ul className="flex flex-wrap items-center gap-4 text-[11px]" style={{ color: "#475569" }}>
165
170
  <li>
166
171
  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: lineColor }} aria-hidden />
167
- {lineLabel}
172
+ {resolvedLineLabel}
168
173
  </li>
169
174
  <li>
170
175
  <span className="h-2.5 w-2.5 rounded-sm" style={{ background: barColor }} aria-hidden />
171
- {barLabel}
176
+ {resolvedBarLabel}
172
177
  </li>
173
178
  </ul>
174
179
  </div>
@@ -2,6 +2,7 @@ import React, { useRef, useState, useMemo } from "react";
2
2
  import clsx from "clsx";
3
3
  import ITInput from "@/components/atoms/input/input";
4
4
  import { ITMaskedInputProps } from "./masked-input.props";
5
+ import { useITTranslation } from "@/i18n";
5
6
 
6
7
  type Token = "9" | "A" | "a" | "x" | "*";
7
8
 
@@ -157,6 +158,7 @@ export default function ITMaskedInput({
157
158
  iconRight,
158
159
  autoFocus = false,
159
160
  }: ITMaskedInputProps) {
161
+ const { t } = useITTranslation();
160
162
  const containerRef = useRef<HTMLDivElement>(null);
161
163
  const rawRef = useRef<string>(value ?? "");
162
164
  const isControlled = value !== undefined;
@@ -215,7 +217,7 @@ export default function ITMaskedInput({
215
217
  const resolvedError = invalidPattern
216
218
  ? typeof error === "string"
217
219
  ? error
218
- : "Formato inválido"
220
+ : t("input.invalidFormat")
219
221
  : error;
220
222
 
221
223
  return (
@@ -8,6 +8,7 @@ import { useFloatingPanel } from "@/hooks/useFloatingPanel";
8
8
  import { inputFieldStyle } from "@/utils/styles";
9
9
  import ITChip from "@/components/atoms/chip/chip";
10
10
  import ITText from "@/components/atoms/text/text";
11
+ import { useITTranslation } from "@/i18n";
11
12
 
12
13
  /**
13
14
  * Multi-value select with searchable dropdown, removable chips, and keyboard navigation.
@@ -39,7 +40,7 @@ import ITText from "@/components/atoms/text/text";
39
40
  export default function ITMultiSelect({
40
41
  name,
41
42
  label,
42
- placeholder = "Selecciona opciones",
43
+ placeholder,
43
44
  options,
44
45
  value,
45
46
  onChange,
@@ -57,10 +58,11 @@ export default function ITMultiSelect({
57
58
  searchable = true,
58
59
  onSearch,
59
60
  isLoading = false,
60
- noResultsMessage = "No se encontraron resultados",
61
+ noResultsMessage,
61
62
  renderOption,
62
63
  className,
63
64
  }: ITMultiSelectProps) {
65
+ const { t } = useITTranslation();
64
66
  const [isOpen, setIsOpen] = useState(false);
65
67
  const [searchTerm, setSearchTerm] = useState("");
66
68
  const [isFocused, setIsFocused] = useState(false);
@@ -110,13 +112,13 @@ export default function ITMultiSelect({
110
112
  const effectiveError =
111
113
  error !== undefined && error !== false
112
114
  ? error === true
113
- ? "Este campo es requerido"
115
+ ? t("field.required")
114
116
  : error
115
117
  : required && isEmpty
116
- ? "Este campo es requerido"
118
+ ? t("field.required")
117
119
  : undefined;
118
120
  const hasError = isTouched && !!effectiveError;
119
- const errorMessage = typeof effectiveError === "string" ? effectiveError : "Este campo es requerido";
121
+ const errorMessage = typeof effectiveError === "string" ? effectiveError : t("field.required");
120
122
 
121
123
  const getControlStyle = (): React.CSSProperties => {
122
124
  const style = inputFieldStyle(inputTheme, size, { width: "100%", cursor: "text" });
@@ -284,7 +286,7 @@ export default function ITMultiSelect({
284
286
  onBlur={handleBlur}
285
287
  disabled={disabled}
286
288
  readOnly={readOnly || !searchable}
287
- placeholder={selectedOptions.length === 0 ? placeholder : ""}
289
+ placeholder={selectedOptions.length === 0 ? (placeholder ?? t("multiSelect.placeholder")) : ""}
288
290
  autoComplete="off"
289
291
  aria-autocomplete="list"
290
292
  aria-activedescendant={activeIndex >= 0 ? `it-multiselect-opt-${activeIndex}` : undefined}
@@ -298,8 +300,8 @@ export default function ITMultiSelect({
298
300
  ) : showClear ? (
299
301
  <button
300
302
  type="button"
301
- aria-label="Limpiar selección"
302
- title="Limpiar selección"
303
+ aria-label={t("common.clearSelection")}
304
+ title={t("common.clearSelection")}
303
305
  onMouseDown={(e) => {
304
306
  e.preventDefault();
305
307
  e.stopPropagation();
@@ -370,7 +372,7 @@ export default function ITMultiSelect({
370
372
  })
371
373
  ) : (
372
374
  <ITText as="div" className="px-4 py-6 text-sm text-center text-secondary-500 italic">
373
- {isLoading ? "Cargando..." : noResultsMessage}
375
+ {isLoading ? t("common.loading") : (noResultsMessage ?? t("select.noResults"))}
374
376
  </ITText>
375
377
  )}
376
378
  </div>
@@ -385,3 +387,4 @@ export default function ITMultiSelect({
385
387
  </div>
386
388
  );
387
389
  }
390
+
@@ -5,6 +5,7 @@ import { ITPaginationProps } from "./pagination.props";
5
5
  import { theme } from "@/theme/theme";
6
6
  import ITSelect from "@/components/molecules/select/select";
7
7
  import ITText from "@/components/atoms/text/text";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  const DOTS = "...";
10
11
 
@@ -103,6 +104,7 @@ export default function ITPagination({
103
104
  onItemsPerPageChange,
104
105
  totalItems,
105
106
  }: ITPaginationProps) {
107
+ const { t } = useITTranslation();
106
108
  const paginationRange = usePagination({
107
109
  currentPage,
108
110
  totalPages,
@@ -151,6 +153,8 @@ export default function ITPagination({
151
153
  )}
152
154
  onClick={handlePrevious}
153
155
  aria-disabled={currentPage === 1}
156
+ aria-label={t("pagination.previous")}
157
+ role="button"
154
158
  >
155
159
  <FaChevronLeft size={12} />
156
160
  </div>
@@ -184,7 +188,7 @@ export default function ITPagination({
184
188
  ...(isActive ? {} : { "--hover-bg": resolvedHoverBgColor } as React.CSSProperties),
185
189
  }}
186
190
  onClick={() => onPageChange(pageNumber as number)}
187
- title={`Page ${pageNumber}`}
191
+ title={t("pagination.page", { page: pageNumber })}
188
192
  >
189
193
  <ITText as="span">{pageNumber}</ITText>
190
194
  </ITText>
@@ -201,6 +205,8 @@ export default function ITPagination({
201
205
  )}
202
206
  onClick={handleNext}
203
207
  aria-disabled={currentPage === totalPages}
208
+ aria-label={t("pagination.next")}
209
+ role="button"
204
210
  >
205
211
  <FaChevronRight size={12} />
206
212
  </div>
@@ -216,7 +222,7 @@ export default function ITPagination({
216
222
  <div className={clsx("flex flex-col sm:flex-row justify-between items-center gap-4 w-full", className)}>
217
223
  <div className="flex items-center gap-4 text-sm text-secondary-500">
218
224
  <div className="flex items-center gap-2 bg-secondary-50 px-3 py-1 rounded-lg border border-secondary-200">
219
- <ITText as="span" className="text-xs font-medium">Mostrar</ITText>
225
+ <ITText as="span" className="text-xs font-medium">{t("pagination.itemsPerPage")}</ITText>
220
226
  <ITSelect
221
227
  name="itemsPerPage"
222
228
  options={itemsPerPageOptions.map((option) => ({
@@ -236,13 +242,13 @@ export default function ITPagination({
236
242
  <>
237
243
  <ITText as="span" className="text-secondary-300">|</ITText>
238
244
  <ITText as="span" className="text-xs">
239
- <ITText as="span" className="font-semibold text-secondary-700">{startItem}</ITText><ITText as="span"> - </ITText><ITText as="span" className="font-semibold text-secondary-700">{endItem}</ITText><ITText as="span"> de </ITText><ITText as="span" className="font-semibold text-secondary-900">{totalItems}</ITText>
245
+ <ITText as="span" className="font-semibold text-secondary-700">{startItem}</ITText><ITText as="span"> - </ITText><ITText as="span" className="font-semibold text-secondary-700">{endItem}</ITText><ITText as="span"> {t("pagination.of")} </ITText><ITText as="span" className="font-semibold text-secondary-900">{totalItems}</ITText>
240
246
  </ITText>
241
247
  </>
242
248
  )}
243
249
  </div>
244
250
 
245
- <nav aria-label="Pagination">
251
+ <nav aria-label={t("pagination.aria")}>
246
252
  {renderPaginationControls()}
247
253
  </nav>
248
254
  </div>
@@ -251,7 +257,7 @@ export default function ITPagination({
251
257
 
252
258
  // Otherwise, render just the standard pagination component
253
259
  return (
254
- <nav aria-label="Pagination" className={clsx("inline-flex", className)}>
260
+ <nav aria-label={t("pagination.aria")} className={clsx("inline-flex", className)}>
255
261
  {renderPaginationControls()}
256
262
  </nav>
257
263
  );
@@ -6,6 +6,7 @@ import { ITSearchSelectProps, ITSearchSelectOption } from "./search-select.props
6
6
  import { theme, inputSizeTokens } from "@/theme/theme";
7
7
  import { useFloatingPanel } from "@/hooks/useFloatingPanel";
8
8
  import ITText from "@/components/atoms/text/text";
9
+ import { useITTranslation } from "@/i18n";
9
10
 
10
11
  /**
11
12
  * Searchable dropdown select with local client-side filtering and server-side search via API.
@@ -52,7 +53,7 @@ export default function ITSearchSelect({
52
53
  name,
53
54
  options = [],
54
55
  label,
55
- placeholder = "Selecciona una opción",
56
+ placeholder,
56
57
  valueField = "value",
57
58
  labelField = "label",
58
59
  value,
@@ -66,12 +67,13 @@ export default function ITSearchSelect({
66
67
  readOnly = false,
67
68
  onSearch,
68
69
  isLoading = false,
69
- noResultsMessage = "No se encontraron resultados",
70
+ noResultsMessage,
70
71
  size = "md",
71
72
  renderOption,
72
73
  clearable = true,
73
74
  onClear,
74
75
  }: ITSearchSelectProps) {
76
+ const { t } = useITTranslation();
75
77
  const [isOpen, setIsOpen] = useState(false);
76
78
  const [searchTerm, setSearchTerm] = useState("");
77
79
  const [isFocused, setIsFocused] = useState(false);
@@ -180,11 +182,11 @@ export default function ITSearchSelect({
180
182
  const isEmpty = value === undefined || value === null || String(value).trim() === "";
181
183
 
182
184
  const effectiveError = error !== undefined && error !== false
183
- ? (error === true ? "Este campo es requerido" : error)
184
- : (required && isEmpty ? "Este campo es requerido" : undefined);
185
+ ? (error === true ? t("field.required") : error)
186
+ : (required && isEmpty ? t("field.required") : undefined);
185
187
 
186
188
  const hasError = isTouched && !!effectiveError;
187
- const errorMessage = typeof effectiveError === "string" ? effectiveError : "Este campo es requerido";
189
+ const errorMessage = typeof effectiveError === "string" ? effectiveError : t("field.required");
188
190
 
189
191
  const showClearButton =
190
192
  clearable && !disabled && !readOnly && (searchTerm.length > 0 || !isEmpty);
@@ -249,7 +251,7 @@ export default function ITSearchSelect({
249
251
  onBlur={handleInputBlur}
250
252
  disabled={disabled}
251
253
  readOnly={readOnly}
252
- placeholder={placeholder}
254
+ placeholder={placeholder ?? t("select.placeholder")}
253
255
  className="outline-none pr-10"
254
256
  style={getInputStyle()}
255
257
  autoComplete="off"
@@ -261,8 +263,8 @@ export default function ITSearchSelect({
261
263
  {!isLoading && showClearButton && (
262
264
  <button
263
265
  type="button"
264
- aria-label="Limpiar"
265
- title="Limpiar"
266
+ aria-label={t("common.clear")}
267
+ title={t("common.clear")}
266
268
  onMouseDown={(e) => {
267
269
  e.preventDefault();
268
270
  handleClear();
@@ -313,7 +315,7 @@ export default function ITSearchSelect({
313
315
  })
314
316
  ) : (
315
317
  <ITText as="div" className="px-4 py-6 text-sm text-center text-secondary-500 italic">
316
- {isLoading ? "Cargando..." : noResultsMessage}
318
+ {isLoading ? t("common.loading") : (noResultsMessage ?? t("select.noResults"))}
317
319
  </ITText>
318
320
  )}
319
321
  </div>
@@ -5,6 +5,7 @@ import { FaAngleDown } from "react-icons/fa";
5
5
  import { ITSelectProps } from "./select.props";
6
6
  import ITText from "@/components/atoms/text/text";
7
7
  import { inputError, inputLabel, inputFieldStyle } from "@/utils/styles";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  export default function ITSelect({
10
11
  name,
@@ -24,6 +25,7 @@ export default function ITSelect({
24
25
  readOnly = false,
25
26
  size = "md",
26
27
  }: ITSelectProps) {
28
+ const { t } = useITTranslation();
27
29
  const [isFocused, setIsFocused] = useState(false);
28
30
  const [localTouched, setLocalTouched] = useState(false);
29
31
 
@@ -34,11 +36,11 @@ export default function ITSelect({
34
36
  const isEmpty = value === undefined || value === null || String(value).trim() === "";
35
37
 
36
38
  const effectiveError = error !== undefined && error !== false
37
- ? (error === true ? "Este campo es requerido" : error)
38
- : (required && isEmpty ? "Este campo es requerido" : undefined);
39
+ ? (error === true ? t("field.required") : error)
40
+ : (required && isEmpty ? t("field.required") : undefined);
39
41
 
40
42
  const hasError = isTouched && !!effectiveError;
41
- const errorMessage = typeof effectiveError === "string" ? effectiveError : "Este campo es requerido";
43
+ const errorMessage = typeof effectiveError === "string" ? effectiveError : t("field.required");
42
44
 
43
45
  const getStyle = () => {
44
46
  const style = inputFieldStyle(inputTheme, size, {
@@ -100,7 +102,7 @@ export default function ITSelect({
100
102
  )}
101
103
  style={getStyle()}
102
104
  >
103
- <option value="">{placeholder || "Selecciona una opción"}</option>
105
+ <option value="">{placeholder || t("select.placeholder")}</option>
104
106
  {
105
107
  readOnly ? (
106
108
  <option value={value} disabled>
@@ -5,6 +5,7 @@ import { theme } from "@/theme/theme";
5
5
  import ITButton from "@/components/atoms/button/button";
6
6
  import { FaChevronLeft, FaChevronRight, FaCheck } from "react-icons/fa";
7
7
  import ITText from "@/components/atoms/text/text";
8
+ import { useITTranslation } from "@/i18n";
8
9
 
9
10
  /**
10
11
  * A step-by-step wizard / progress indicator with animated content transitions,
@@ -35,6 +36,7 @@ export default function ITStepper({
35
36
  maxContentHeight = "400px",
36
37
  color = "primary",
37
38
  }: ITStepperProps) {
39
+ const { t } = useITTranslation();
38
40
  const [direction, setDirection] = useState<"next" | "prev">("next");
39
41
  const contentRef = useRef<HTMLDivElement | null>(null);
40
42
  const progressRef = useRef<HTMLDivElement | null>(null);
@@ -149,7 +151,7 @@ export default function ITStepper({
149
151
  onClick={() => jumpTo(idx)}
150
152
  disabled={!allowClickToJump && idx !== currentStep}
151
153
  aria-current={isActive ? "step" : undefined}
152
- aria-label={`Paso ${idx + 1} ${step.label}`}
154
+ aria-label={t("stepper.stepAria", { n: idx + 1, label: step.label })}
153
155
  className="flex-1 group"
154
156
  title={step.label}
155
157
  >
@@ -214,13 +216,13 @@ export default function ITStepper({
214
216
  >
215
217
  <div className="flex items-center gap-2">
216
218
  <FaChevronLeft />
217
- <ITText as="span">Atrás</ITText>
219
+ <ITText as="span">{t("common.back")}</ITText>
218
220
  </div>
219
221
  </ITButton>
220
222
 
221
223
  <div className="flex items-center gap-3">
222
224
  <ITText as="div" className="text-sm text-secondary-500 mr-2 hidden sm:block">
223
- Paso {currentStep + 1} de {steps.length}
225
+ {t("stepper.stepOf", { current: currentStep + 1, total: steps.length })}
224
226
  </ITText>
225
227
 
226
228
  <ITButton
@@ -230,7 +232,7 @@ export default function ITStepper({
230
232
  onClick={nextStep}
231
233
  >
232
234
  <div className="flex items-center gap-2">
233
- <ITText as="span">{currentStep === steps.length - 1 ? "Finalizar" : "Siguiente"}</ITText>
235
+ <ITText as="span">{currentStep === steps.length - 1 ? t("common.finish") : t("common.next")}</ITText>
234
236
  {currentStep === steps.length - 1 ? <FaCheck /> : <FaChevronRight />}
235
237
  </div>
236
238
  </ITButton>