@axzydev/axzy_ui_system 1.4.8 → 1.5.0
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 +77 -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 +1929 -0
- package/src/sandbox/icons.tsx +25 -0
- package/src/sandbox/locale.tsx +72 -0
- package/src/sandbox/ui.tsx +377 -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 +302 -214
- 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 +270 -216
- package/src/showcases/SizesShowcase.tsx +123 -64
- package/src/showcases/StructureShowcases.tsx +157 -131
- 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
|
@@ -3,6 +3,7 @@ import ITButton from "@/components/atoms/button/button";
|
|
|
3
3
|
import ITFlex from "@/components/atoms/flex/flex";
|
|
4
4
|
import ITStack from "@/components/atoms/stack/stack";
|
|
5
5
|
import ITText from "@/components/atoms/text/text";
|
|
6
|
+
import { useITTranslation } from "@/i18n";
|
|
6
7
|
|
|
7
8
|
/** Props for the sandbox 404 page. */
|
|
8
9
|
export interface NotFoundShowcaseProps {
|
|
@@ -18,53 +19,67 @@ export interface NotFoundShowcaseProps {
|
|
|
18
19
|
* Sandbox 404 page rendered when a hash route does not resolve to a page.
|
|
19
20
|
*
|
|
20
21
|
* Shows the attempted path, a friendly message, and buttons to recover.
|
|
22
|
+
* Follows the blueprint spec: flat surface, 1px border, mono metadata.
|
|
21
23
|
*/
|
|
22
24
|
export default function NotFoundShowcase({
|
|
23
25
|
attempted,
|
|
24
26
|
onGoLanding,
|
|
25
27
|
onGoPortfolio,
|
|
26
28
|
}: NotFoundShowcaseProps) {
|
|
29
|
+
const { t } = useITTranslation();
|
|
27
30
|
return (
|
|
28
31
|
<div className="animate-fadeIn flex min-h-[60vh] items-center justify-center px-4">
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<ITStack spacing={2} className="items-center">
|
|
38
|
-
<ITText as="h1" className="!text-2xl !font-bold text-slate-900 dark:text-white">
|
|
39
|
-
Ruta no encontrada
|
|
40
|
-
</ITText>
|
|
41
|
-
<ITText as="p" className="!text-sm text-slate-500 dark:text-slate-400">
|
|
42
|
-
No existe una página para este hash. Revisa el enlace o vuelve al inicio.
|
|
32
|
+
<div className="it-reveal w-full max-w-xl rounded-2xl border border-slate-200 bg-white p-8 text-center dark:border-slate-800 dark:bg-slate-900 sm:p-10">
|
|
33
|
+
<ITStack spacing={5} className="items-center">
|
|
34
|
+
<ITText
|
|
35
|
+
as="p"
|
|
36
|
+
className="font-mono !text-[11px] !font-medium uppercase !tracking-[0.18em] text-primary-600 dark:text-primary-400"
|
|
37
|
+
>
|
|
38
|
+
{t("showcase.notFound.eyebrow")}
|
|
43
39
|
</ITText>
|
|
44
|
-
<code className="mt-1 max-w-full truncate rounded-lg border border-slate-200 bg-slate-50 px-3 py-1.5 font-mono text-xs text-slate-600 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300">
|
|
45
|
-
{attempted || "#"}
|
|
46
|
-
</code>
|
|
47
|
-
</ITStack>
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
color="primary"
|
|
53
|
-
icon={<FaHome size={13} />}
|
|
54
|
-
onClick={onGoLanding}
|
|
55
|
-
>
|
|
56
|
-
Volver al inicio
|
|
57
|
-
</ITButton>
|
|
58
|
-
<ITButton
|
|
59
|
-
variant="outlined"
|
|
60
|
-
color="secondary"
|
|
61
|
-
icon={<FaLayerGroup size={13} />}
|
|
62
|
-
onClick={onGoPortfolio}
|
|
41
|
+
<ITText
|
|
42
|
+
as="div"
|
|
43
|
+
className="font-display !text-6xl !font-bold -tracking-[0.02em] tabular-nums text-slate-900 dark:text-white"
|
|
63
44
|
>
|
|
64
|
-
|
|
65
|
-
</
|
|
66
|
-
|
|
67
|
-
|
|
45
|
+
404
|
|
46
|
+
</ITText>
|
|
47
|
+
|
|
48
|
+
<ITStack spacing={2} className="items-center">
|
|
49
|
+
<ITText
|
|
50
|
+
as="h1"
|
|
51
|
+
className="font-display !text-2xl !font-bold -tracking-[0.02em] text-slate-900 dark:text-white"
|
|
52
|
+
>
|
|
53
|
+
{t("showcase.notFound.title")}
|
|
54
|
+
</ITText>
|
|
55
|
+
<ITText as="p" className="!text-sm !leading-relaxed text-slate-500 dark:text-slate-400">
|
|
56
|
+
{t("showcase.notFound.description")}
|
|
57
|
+
</ITText>
|
|
58
|
+
<code className="mt-1 max-w-full truncate rounded-[10px] border border-slate-200 bg-slate-50 px-3 py-1.5 font-mono text-xs text-slate-600 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-300">
|
|
59
|
+
{attempted || "#"}
|
|
60
|
+
</code>
|
|
61
|
+
</ITStack>
|
|
62
|
+
|
|
63
|
+
<ITFlex gap={3} wrap="wrap" justify="center" align="center">
|
|
64
|
+
<ITButton
|
|
65
|
+
variant="filled"
|
|
66
|
+
color="primary"
|
|
67
|
+
icon={<FaHome size={13} />}
|
|
68
|
+
onClick={onGoLanding}
|
|
69
|
+
>
|
|
70
|
+
{t("showcase.notFound.backHome")}
|
|
71
|
+
</ITButton>
|
|
72
|
+
<ITButton
|
|
73
|
+
variant="outlined"
|
|
74
|
+
color="secondary"
|
|
75
|
+
icon={<FaLayerGroup size={13} />}
|
|
76
|
+
onClick={onGoPortfolio}
|
|
77
|
+
>
|
|
78
|
+
{t("showcase.landing.viewPortfolio")}
|
|
79
|
+
</ITButton>
|
|
80
|
+
</ITFlex>
|
|
81
|
+
</ITStack>
|
|
82
|
+
</div>
|
|
68
83
|
</div>
|
|
69
84
|
);
|
|
70
85
|
}
|
|
@@ -2,6 +2,7 @@ import type { ChangeEvent } from "react";
|
|
|
2
2
|
import { FaSearch, FaTimes } from "react-icons/fa";
|
|
3
3
|
import ITButton from "@/components/atoms/button/button";
|
|
4
4
|
import ITInput from "@/components/atoms/input/input";
|
|
5
|
+
import { useITTranslation } from "@/i18n";
|
|
5
6
|
|
|
6
7
|
/** Props for the sandbox topbar search filter. */
|
|
7
8
|
export interface TopbarSearchProps {
|
|
@@ -37,6 +38,7 @@ export default function TopbarSearch({
|
|
|
37
38
|
pending = false,
|
|
38
39
|
className = "w-full",
|
|
39
40
|
}: TopbarSearchProps) {
|
|
41
|
+
const { t } = useITTranslation();
|
|
40
42
|
const isActive = value.trim().length > 0;
|
|
41
43
|
const showCount = isActive && !pending;
|
|
42
44
|
|
|
@@ -50,9 +52,9 @@ export default function TopbarSearch({
|
|
|
50
52
|
<div className="flex-1 min-w-0">
|
|
51
53
|
<ITInput
|
|
52
54
|
name="sandbox-topbar-search"
|
|
53
|
-
label="
|
|
55
|
+
label={t("sandbox.search.label")}
|
|
54
56
|
labelClassName="sr-only"
|
|
55
|
-
placeholder="
|
|
57
|
+
placeholder={t("sandbox.search.placeholder")}
|
|
56
58
|
value={value}
|
|
57
59
|
onChange={(event: ChangeEvent<HTMLInputElement>) =>
|
|
58
60
|
onChange(event.target.value)
|
|
@@ -68,7 +70,7 @@ export default function TopbarSearch({
|
|
|
68
70
|
variant="text"
|
|
69
71
|
color="danger"
|
|
70
72
|
size="sm"
|
|
71
|
-
ariaLabel="
|
|
73
|
+
ariaLabel={t("sandbox.search.clear")}
|
|
72
74
|
onClick={onClear}
|
|
73
75
|
className="flex-shrink-0"
|
|
74
76
|
>
|
|
@@ -78,7 +80,7 @@ export default function TopbarSearch({
|
|
|
78
80
|
</div>
|
|
79
81
|
|
|
80
82
|
<span role="status" aria-live="polite" className="sr-only">
|
|
81
|
-
{showCount ?
|
|
83
|
+
{showCount ? t("sandbox.search.results", { count: resultCount }) : ""}
|
|
82
84
|
</span>
|
|
83
85
|
</form>
|
|
84
86
|
);
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controles de chrome del sandbox: idioma y modo de color.
|
|
3
|
+
*
|
|
4
|
+
* Viven aquí (y no en `showcases/`) porque los usan las dos superficies: la
|
|
5
|
+
* barra del sandbox (`ITTopBar`, en `App.tsx`) y la cabecera del portafolio
|
|
6
|
+
* (`HomeShowcase` / `ProductoDetalle`).
|
|
7
|
+
*
|
|
8
|
+
* No se usa `ITSegmentedControl` de la librería: su opción inactiva queda en
|
|
9
|
+
* 4.34:1 sobre `slate-100` y falla AA con la paleta por defecto. Estas opciones
|
|
10
|
+
* van en `slate-600`/`slate-300` (≥ 7:1) y el activo en `primary-700` con texto
|
|
11
|
+
* blanco (5:1).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { ReactNode } from "react";
|
|
15
|
+
import { FaDesktop, FaMoon, FaSun } from "react-icons/fa";
|
|
16
|
+
import { useITTranslation } from "@/i18n";
|
|
17
|
+
import { useITThemeSafe } from "@/theme/theme-context";
|
|
18
|
+
import { useSandboxLocale } from "./locale";
|
|
19
|
+
|
|
20
|
+
/** Una opción del control segmentado. */
|
|
21
|
+
export interface OpcionSegmento {
|
|
22
|
+
/** Valor que emite al pulsarla. */
|
|
23
|
+
value: string;
|
|
24
|
+
/** Texto visible. También es el nombre accesible cuando la opción es solo ícono. */
|
|
25
|
+
label: string;
|
|
26
|
+
/** Ícono opcional; cuando existe, la opción se pinta cuadrada y solo con ícono. */
|
|
27
|
+
icono?: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Props de `ControlSegmentado`. */
|
|
31
|
+
export interface ControlSegmentadoProps {
|
|
32
|
+
/** Opciones mutuamente excluyentes. */
|
|
33
|
+
opciones: OpcionSegmento[];
|
|
34
|
+
/** Valor activo. */
|
|
35
|
+
valor: string;
|
|
36
|
+
/** Se dispara con el valor elegido. */
|
|
37
|
+
onChange: (value: string) => void;
|
|
38
|
+
/** Nombre accesible del grupo. */
|
|
39
|
+
etiqueta: string;
|
|
40
|
+
/** Clases extra del contenedor. */
|
|
41
|
+
className?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Pinta el texto junto al ícono (en vez de solo el ícono) y reparte el ancho
|
|
44
|
+
* entre las opciones. Para barras de pestañas; en controles compactos
|
|
45
|
+
* (cabecera, tema) se deja en `false`.
|
|
46
|
+
* @default false
|
|
47
|
+
*/
|
|
48
|
+
conEtiqueta?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Grupo de opciones excluyentes en la lengua visual del proyecto: borde de 1px,
|
|
53
|
+
* mono en mayúsculas y activo relleno del acento.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* <ControlSegmentado
|
|
58
|
+
* etiqueta={t("sandbox.language")}
|
|
59
|
+
* valor={locale}
|
|
60
|
+
* onChange={setLocale}
|
|
61
|
+
* opciones={[{ value: "es", label: "ES" }, { value: "en", label: "EN" }]}
|
|
62
|
+
* />
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export const ControlSegmentado = ({
|
|
66
|
+
opciones,
|
|
67
|
+
valor,
|
|
68
|
+
onChange,
|
|
69
|
+
etiqueta,
|
|
70
|
+
className = "",
|
|
71
|
+
conEtiqueta = false,
|
|
72
|
+
}: ControlSegmentadoProps) => (
|
|
73
|
+
<div
|
|
74
|
+
role="group"
|
|
75
|
+
aria-label={etiqueta}
|
|
76
|
+
className={`inline-flex shrink-0 items-center gap-0.5 rounded-[10px] border border-slate-200 bg-white p-0.5 dark:border-slate-800 dark:bg-slate-900 ${className}`}
|
|
77
|
+
>
|
|
78
|
+
{opciones.map((opcion) => {
|
|
79
|
+
const activo = opcion.value === valor;
|
|
80
|
+
return (
|
|
81
|
+
<button
|
|
82
|
+
key={opcion.value}
|
|
83
|
+
type="button"
|
|
84
|
+
onClick={() => onChange(opcion.value)}
|
|
85
|
+
aria-pressed={activo}
|
|
86
|
+
title={opcion.label}
|
|
87
|
+
className={`inline-flex h-7 items-center justify-center gap-1.5 rounded-[7px] font-mono text-[10px] font-medium uppercase tracking-[0.1em] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/60 ${
|
|
88
|
+
conEtiqueta ? "flex-1 px-3" : opcion.icono ? "w-7 px-0" : "px-2.5"
|
|
89
|
+
} ${
|
|
90
|
+
activo
|
|
91
|
+
? "bg-primary-700 text-white dark:bg-primary-500 dark:text-slate-950"
|
|
92
|
+
: "text-slate-600 hover:text-slate-900 dark:text-slate-300 dark:hover:text-white"
|
|
93
|
+
}`}
|
|
94
|
+
>
|
|
95
|
+
{opcion.icono}
|
|
96
|
+
{conEtiqueta || !opcion.icono ? (
|
|
97
|
+
<span>{opcion.label}</span>
|
|
98
|
+
) : (
|
|
99
|
+
<span className="sr-only">{opcion.label}</span>
|
|
100
|
+
)}
|
|
101
|
+
</button>
|
|
102
|
+
);
|
|
103
|
+
})}
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Selector de idioma (ES/EN). Comparte estado con cualquier otro selector de la
|
|
109
|
+
* página a través de `useSandboxLocale`.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```tsx
|
|
113
|
+
* <SelectorIdioma />
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export const SelectorIdioma = ({ className = "" }: { className?: string }) => {
|
|
117
|
+
const { t } = useITTranslation();
|
|
118
|
+
const { locale, setLocale } = useSandboxLocale();
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<ControlSegmentado
|
|
122
|
+
className={className}
|
|
123
|
+
etiqueta={t("sandbox.language")}
|
|
124
|
+
valor={locale}
|
|
125
|
+
onChange={(value) => setLocale(value === "en" ? "en" : "es")}
|
|
126
|
+
opciones={[
|
|
127
|
+
{ value: "es", label: "ES" },
|
|
128
|
+
{ value: "en", label: "EN" },
|
|
129
|
+
]}
|
|
130
|
+
/>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Selector de modo de color: claro, oscuro o el del sistema. Si no hay
|
|
136
|
+
* `ITThemeProvider` encima no se pinta (no rompe el render aislado).
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```tsx
|
|
140
|
+
* <SelectorTema />
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export const SelectorTema = ({ className = "" }: { className?: string }) => {
|
|
144
|
+
const { t } = useITTranslation();
|
|
145
|
+
const theme = useITThemeSafe();
|
|
146
|
+
|
|
147
|
+
if (!theme) return null;
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<ControlSegmentado
|
|
151
|
+
className={className}
|
|
152
|
+
etiqueta={t("sandbox.theme")}
|
|
153
|
+
valor={theme.darkModeMode}
|
|
154
|
+
onChange={(value) =>
|
|
155
|
+
theme.setDarkModeMode(value === "dark" ? "dark" : value === "light" ? "light" : "system")
|
|
156
|
+
}
|
|
157
|
+
opciones={[
|
|
158
|
+
{ value: "light", label: t("sandbox.themeLight"), icono: <FaSun size={11} /> },
|
|
159
|
+
{ value: "dark", label: t("sandbox.themeDark"), icono: <FaMoon size={11} /> },
|
|
160
|
+
{ value: "system", label: t("sandbox.themeSystem"), icono: <FaDesktop size={11} /> },
|
|
161
|
+
]}
|
|
162
|
+
/>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Par de controles (idioma + tema) tal como se pintan en la cabecera del
|
|
168
|
+
* portafolio.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```tsx
|
|
172
|
+
* <header><nav>…<ControlesPortafolio /></nav></header>
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
export const ControlesPortafolio = ({ className = "" }: { className?: string }) => (
|
|
176
|
+
<div className={`flex shrink-0 items-center gap-2 ${className}`}>
|
|
177
|
+
<SelectorIdioma />
|
|
178
|
+
<SelectorTema />
|
|
179
|
+
</div>
|
|
180
|
+
);
|