@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.
- package/README.md +2 -11
- package/dist/index.cjs +1826 -1351
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +115 -14
- package/dist/index.d.ts +115 -14
- package/dist/index.js +1481 -1010
- package/dist/index.js.map +1 -1
- package/dist/layered.css +1 -1
- package/package.json +3 -16
- package/src/App.tsx +90 -73
- package/src/components/atoms/badget/badget.tsx +23 -5
- package/src/components/atoms/chip/chip.tsx +3 -1
- package/src/components/atoms/image/image.tsx +3 -1
- package/src/components/atoms/input/input.tsx +8 -5
- package/src/components/atoms/loader/loader.tsx +3 -1
- package/src/components/atoms/slide/slide.props.ts +12 -0
- package/src/components/atoms/slide/slide.tsx +3 -0
- package/src/components/molecules/alert/alert.tsx +3 -1
- package/src/components/molecules/breadcrumbs/breadcrumbs.tsx +3 -1
- package/src/components/molecules/calendar/calendar.tsx +9 -6
- package/src/components/molecules/card/card.tsx +5 -2
- package/src/components/molecules/chip-input/chip-input.tsx +9 -7
- package/src/components/molecules/date-picker/datePicker.tsx +9 -9
- package/src/components/molecules/dropdown-menu/dropdown-menu.props.ts +1 -1
- package/src/components/molecules/dropdown-menu/dropdown-menu.tsx +6 -3
- package/src/components/molecules/field/field.tsx +3 -1
- package/src/components/molecules/form-header/form-header.tsx +3 -1
- package/src/components/molecules/line-bar-chart/line-bar-chart.props.ts +2 -2
- package/src/components/molecules/line-bar-chart/line-bar-chart.tsx +20 -15
- package/src/components/molecules/masked-input/masked-input.tsx +3 -1
- package/src/components/molecules/multi-select/multi-select.tsx +12 -9
- package/src/components/molecules/pagination/pagination.tsx +11 -5
- package/src/components/molecules/search-select/search-select.tsx +11 -9
- package/src/components/molecules/select/select.tsx +6 -4
- package/src/components/molecules/stepper/stepper.tsx +6 -4
- package/src/components/molecules/table/table.tsx +30 -28
- package/src/components/molecules/time-picker/timePicker.tsx +8 -6
- package/src/components/molecules/wysiwyg/wysiwyg.tsx +23 -12
- package/src/components/organisms/confirm-dialog/confirm-dialog.props.ts +4 -4
- package/src/components/organisms/confirm-dialog/confirm-dialog.tsx +16 -8
- package/src/components/organisms/data-table/dataTable.tsx +31 -25
- package/src/components/organisms/dialog/dialog.tsx +3 -0
- package/src/components/organisms/drawer/drawer.tsx +3 -0
- package/src/components/organisms/dropfile/dropfile.tsx +19 -17
- package/src/components/organisms/form-builder/fieldRenderer.tsx +4 -2
- package/src/components/organisms/navbar/navbar.tsx +4 -2
- package/src/components/organisms/page-header/page-header.tsx +3 -1
- package/src/components/organisms/searchTable/components/PaginationControls.tsx +8 -6
- package/src/components/organisms/searchTable/components/PaginationInfo.tsx +3 -1
- package/src/components/organisms/searchTable/components/SearchAndSortBar.tsx +7 -5
- package/src/components/organisms/searchTable/components/SearchInput.tsx +4 -2
- package/src/components/organisms/searchTable/components/SortButton.tsx +13 -11
- package/src/components/organisms/searchTable/components/TableEmptyState.tsx +4 -2
- package/src/components/organisms/searchTable/components/TableRow.tsx +6 -4
- package/src/components/organisms/searchTable/searchTable.tsx +4 -2
- package/src/components/organisms/sidebar/sidebar.props.ts +1 -1
- package/src/components/organisms/sidebar/sidebar.tsx +9 -4
- package/src/components/organisms/toast/toast.tsx +3 -1
- package/src/components/organisms/topbar/topbar.tsx +4 -2
- package/src/components/templates/page/page.props.ts +4 -4
- package/src/components/templates/page/page.tsx +6 -4
- package/src/components/theme-provider/themeProvider.tsx +28 -17
- package/src/dev.css +61 -2
- package/src/global.d.ts +10 -0
- package/src/i18n/context.ts +9 -0
- package/src/i18n/index.ts +22 -0
- package/src/i18n/locales/en.ts +163 -0
- package/src/i18n/locales/es.ts +165 -0
- package/src/i18n/provider.tsx +44 -0
- package/src/i18n/translator.ts +82 -0
- package/src/i18n/types.ts +54 -0
- package/src/i18n/useITTranslation.ts +20 -0
- package/src/index.css +0 -1
- package/src/index.ts +7 -1
- package/src/sandbox/LandingShowcase.tsx +224 -580
- package/src/sandbox/NotFoundShowcase.tsx +51 -36
- package/src/sandbox/TopbarSearch.tsx +6 -4
- package/src/sandbox/controls.tsx +180 -0
- package/src/sandbox/i18n.ts +1939 -0
- package/src/sandbox/icons.tsx +25 -0
- package/src/sandbox/locale.tsx +72 -0
- package/src/sandbox/ui.tsx +474 -0
- package/src/sandbox/useHashRoute.ts +46 -10
- package/src/showcases/DataShowcases.tsx +103 -87
- package/src/showcases/FeedbackShowcases.tsx +37 -30
- package/src/showcases/FormShowcases.tsx +303 -215
- package/src/showcases/GettingStartedShowcase.tsx +151 -145
- package/src/showcases/HelpShowcase.tsx +126 -128
- package/src/showcases/HomeShowcase.tsx +678 -482
- package/src/showcases/LayoutPrimitivesShowcases.tsx +129 -114
- package/src/showcases/NavigationShowcases.tsx +45 -37
- package/src/showcases/PageShowcases.tsx +171 -152
- package/src/showcases/ProductoDetalle.tsx +272 -129
- package/src/showcases/ShowcaseLayout.tsx +256 -219
- package/src/showcases/SizesShowcase.tsx +123 -64
- package/src/showcases/StructureShowcases.tsx +203 -138
- package/src/showcases/portfolioUi.tsx +153 -0
- package/src/showcases/techIcons.ts +3 -0
- package/src/utils/table.utils.ts +10 -2
- package/src/components/atoms/avatar/avatar.doc.mdx +0 -48
- package/src/components/atoms/avatar/avatar.stories.tsx +0 -64
- package/src/components/atoms/badget/badget.doc.mdx +0 -46
- package/src/components/atoms/badget/badget.stories.tsx +0 -75
- package/src/components/atoms/button/button.doc.mdx +0 -59
- package/src/components/atoms/button/button.stories.tsx +0 -173
- package/src/components/atoms/checkbox/checkbox.doc.mdx +0 -54
- package/src/components/atoms/checkbox/checkbox.stories.tsx +0 -53
- package/src/components/atoms/chip/chip.doc.mdx +0 -47
- package/src/components/atoms/chip/chip.stories.tsx +0 -64
- package/src/components/atoms/divider/divider.doc.mdx +0 -42
- package/src/components/atoms/divider/divider.stories.tsx +0 -33
- package/src/components/atoms/flex/flex.doc.mdx +0 -59
- package/src/components/atoms/flex/flex.stories.tsx +0 -70
- package/src/components/atoms/grid/grid.doc.mdx +0 -54
- package/src/components/atoms/grid/grid.stories.tsx +0 -71
- package/src/components/atoms/image/image.doc.mdx +0 -44
- package/src/components/atoms/image/image.stories.tsx +0 -48
- package/src/components/atoms/input/input.doc.mdx +0 -70
- package/src/components/atoms/input/input.stories.tsx +0 -113
- package/src/components/atoms/input-number/input-number.doc.mdx +0 -74
- package/src/components/atoms/input-number/input-number.stories.tsx +0 -111
- package/src/components/atoms/loader/loader.doc.mdx +0 -43
- package/src/components/atoms/loader/loader.stories.tsx +0 -104
- package/src/components/atoms/popover/popover.doc.mdx +0 -53
- package/src/components/atoms/popover/popover.stories.tsx +0 -24
- package/src/components/atoms/progress/progress.doc.mdx +0 -48
- package/src/components/atoms/progress/progress.stories.tsx +0 -39
- package/src/components/atoms/radio/radio.doc.mdx +0 -62
- package/src/components/atoms/radio/radio.stories.tsx +0 -48
- package/src/components/atoms/segmented-control/segmented-control.doc.mdx +0 -60
- package/src/components/atoms/segmented-control/segmented-control.stories.tsx +0 -62
- package/src/components/atoms/skeleton/skeleton.doc.mdx +0 -43
- package/src/components/atoms/skeleton/skeleton.stories.tsx +0 -29
- package/src/components/atoms/slide/slide.doc.mdx +0 -46
- package/src/components/atoms/slide/slide.stories.tsx +0 -120
- package/src/components/atoms/slider/slider.doc.mdx +0 -49
- package/src/components/atoms/slider/slider.stories.tsx +0 -29
- package/src/components/atoms/stack/stack.doc.mdx +0 -126
- package/src/components/atoms/stack/stack.stories.tsx +0 -283
- package/src/components/atoms/text/text.doc.mdx +0 -41
- package/src/components/atoms/text/text.stories.tsx +0 -66
- package/src/components/atoms/textarea/textarea.doc.mdx +0 -64
- package/src/components/atoms/textarea/textarea.stories.tsx +0 -26
- package/src/components/atoms/tooltip/tooltip.doc.mdx +0 -47
- package/src/components/atoms/tooltip/tooltip.stories.tsx +0 -19
- package/src/components/isolation/isolation.stories.tsx +0 -202
- package/src/components/molecules/accordion/accordion.doc.mdx +0 -47
- package/src/components/molecules/accordion/accordion.stories.tsx +0 -68
- package/src/components/molecules/alert/alert.doc.mdx +0 -48
- package/src/components/molecules/alert/alert.stories.tsx +0 -40
- package/src/components/molecules/breadcrumbs/breadcrumbs.doc.mdx +0 -57
- package/src/components/molecules/breadcrumbs/breadcrumbs.stories.tsx +0 -20
- package/src/components/molecules/calendar/calendar.doc.mdx +0 -76
- package/src/components/molecules/calendar/calendar.stories.tsx +0 -90
- package/src/components/molecules/card/card.doc.mdx +0 -54
- package/src/components/molecules/card/card.stories.tsx +0 -57
- package/src/components/molecules/chip-input/chip-input.doc.mdx +0 -44
- package/src/components/molecules/chip-input/chip-input.stories.tsx +0 -52
- package/src/components/molecules/date-picker/date-picker.doc.mdx +0 -65
- package/src/components/molecules/date-picker/date-picker.stories.tsx +0 -88
- package/src/components/molecules/dropdown-menu/dropdown-menu.doc.mdx +0 -46
- package/src/components/molecules/dropdown-menu/dropdown-menu.stories.tsx +0 -41
- package/src/components/molecules/empty-state/empty-state.doc.mdx +0 -50
- package/src/components/molecules/empty-state/empty-state.stories.tsx +0 -19
- package/src/components/molecules/field/field.doc.mdx +0 -43
- package/src/components/molecules/field/field.stories.tsx +0 -42
- package/src/components/molecules/form-header/form-header.doc.mdx +0 -38
- package/src/components/molecules/form-header/form-header.stories.tsx +0 -52
- package/src/components/molecules/masked-input/masked-input.doc.mdx +0 -78
- package/src/components/molecules/masked-input/masked-input.stories.tsx +0 -126
- package/src/components/molecules/multi-select/multi-select.doc.mdx +0 -55
- package/src/components/molecules/multi-select/multi-select.stories.tsx +0 -59
- package/src/components/molecules/pagination/pagination.doc.mdx +0 -62
- package/src/components/molecules/pagination/pagination.stories.tsx +0 -110
- package/src/components/molecules/search-select/search-select.doc.mdx +0 -77
- package/src/components/molecules/search-select/search-select.stories.tsx +0 -146
- package/src/components/molecules/select/select.doc.mdx +0 -75
- package/src/components/molecules/select/select.stories.tsx +0 -85
- package/src/components/molecules/stat-card/stat-card.doc.mdx +0 -48
- package/src/components/molecules/stat-card/stat-card.stories.tsx +0 -40
- package/src/components/molecules/stepper/stepper.doc.mdx +0 -54
- package/src/components/molecules/stepper/stepper.stories.tsx +0 -154
- package/src/components/molecules/table/table.doc.mdx +0 -140
- package/src/components/molecules/table/table.stories.tsx +0 -402
- package/src/components/molecules/tabs/tabs.doc.mdx +0 -52
- package/src/components/molecules/tabs/tabs.stories.tsx +0 -31
- package/src/components/molecules/time-picker/timePicker.doc.mdx +0 -68
- package/src/components/molecules/time-picker/timePicker.stories.tsx +0 -130
- package/src/components/molecules/wysiwyg/wysiwyg.stories.tsx +0 -95
- package/src/components/organisms/confirm-dialog/confirm-dialog.doc.mdx +0 -60
- package/src/components/organisms/confirm-dialog/confirm-dialog.stories.tsx +0 -32
- package/src/components/organisms/data-table/ITDataTable.doc.mdx +0 -132
- package/src/components/organisms/data-table/ITDataTable.stories.tsx +0 -353
- package/src/components/organisms/dialog/dialog.doc.mdx +0 -54
- package/src/components/organisms/dialog/dialog.stories.tsx +0 -126
- package/src/components/organisms/drawer/drawer.doc.mdx +0 -59
- package/src/components/organisms/drawer/drawer.stories.tsx +0 -40
- package/src/components/organisms/dropfile/dropfile.doc.mdx +0 -74
- package/src/components/organisms/dropfile/dropfile.stories.tsx +0 -93
- package/src/components/organisms/form-builder/formBuilder.doc.mdx +0 -251
- package/src/components/organisms/form-builder/formBuilder.stories.tsx +0 -914
- package/src/components/organisms/navbar/navbar.doc.mdx +0 -77
- package/src/components/organisms/navbar/navbar.stories.tsx +0 -83
- package/src/components/organisms/page-header/page-header.doc.mdx +0 -54
- package/src/components/organisms/page-header/page-header.stories.tsx +0 -74
- package/src/components/organisms/searchTable/searchTable.doc.mdx +0 -79
- package/src/components/organisms/searchTable/searchTable.stories.tsx +0 -73
- package/src/components/organisms/sidebar/sidebar.doc.mdx +0 -176
- package/src/components/organisms/sidebar/sidebar.stories.tsx +0 -234
- package/src/components/organisms/toast/toast.doc.mdx +0 -50
- package/src/components/organisms/toast/toast.stories.tsx +0 -137
- package/src/components/organisms/topbar/topbar.doc.mdx +0 -72
- package/src/components/organisms/topbar/topbar.stories.tsx +0 -79
- package/src/components/organisms/triple-filter/tripleFilter.doc.mdx +0 -62
- package/src/components/organisms/triple-filter/tripleFilter.stories.tsx +0 -31
- package/src/components/templates/layout/layout.doc.mdx +0 -53
- package/src/components/templates/layout/layout.stories.tsx +0 -113
- package/src/components/templates/page/page.doc.mdx +0 -86
- package/src/components/templates/page/page.stories.tsx +0 -97
- package/src/components/theme-provider/themeProvider.doc.mdx +0 -85
- 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
|
|
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
|
-
? "
|
|
58
|
+
? t("field.required")
|
|
57
59
|
: error
|
|
58
60
|
: required && value.length === 0
|
|
59
|
-
? "
|
|
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(
|
|
93
|
+
setLocalError(t("chipInput.maxTags", { count: maxTags }));
|
|
92
94
|
return;
|
|
93
95
|
}
|
|
94
96
|
if (!allowDuplicates && value.includes(tag)) {
|
|
95
|
-
setLocalError("
|
|
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((
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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 ? "
|
|
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
|
|
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={
|
|
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={
|
|
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 ? "
|
|
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="
|
|
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
|
|
21
|
-
barLabel
|
|
15
|
+
lineLabel,
|
|
16
|
+
barLabel,
|
|
22
17
|
lineColor = "#10b981",
|
|
23
18
|
barColor = "#3b82f6",
|
|
24
19
|
height = 240,
|
|
25
|
-
formatValue
|
|
26
|
-
formatAxis
|
|
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
|
-
{
|
|
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
|
-
{
|
|
161
|
+
{resolvedLineLabel}: {resolvedFormatValue(active.line)}
|
|
157
162
|
</p>
|
|
158
163
|
<p className="text-[11px] tabular-nums" style={{ color: "#93c5fd" }}>
|
|
159
|
-
{
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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
|
-
: "
|
|
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
|
|
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
|
|
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
|
-
? "
|
|
115
|
+
? t("field.required")
|
|
114
116
|
: error
|
|
115
117
|
: required && isEmpty
|
|
116
|
-
? "
|
|
118
|
+
? t("field.required")
|
|
117
119
|
: undefined;
|
|
118
120
|
const hasError = isTouched && !!effectiveError;
|
|
119
|
-
const errorMessage = typeof effectiveError === "string" ? effectiveError : "
|
|
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="
|
|
302
|
-
title="
|
|
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 ? "
|
|
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={
|
|
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">
|
|
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">
|
|
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="
|
|
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="
|
|
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
|
|
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
|
|
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 ? "
|
|
184
|
-
: (required && isEmpty ? "
|
|
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 : "
|
|
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="
|
|
265
|
-
title="
|
|
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 ? "
|
|
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 ? "
|
|
38
|
-
: (required && isEmpty ? "
|
|
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 : "
|
|
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 || "
|
|
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={
|
|
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">
|
|
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
|
-
|
|
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 ? "
|
|
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>
|