@stackcraftmx/calcita 0.28.0 → 0.30.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 +139 -0
- package/dist/DataTable/AuditTrailDialog.js +18 -14
- package/dist/DataTable/AuditTrailDialog.js.map +1 -1
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js +12 -6
- package/dist/DataTable/ColumnFilters/HeaderFilterPopover.js.map +1 -1
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js +8 -3
- package/dist/DataTable/ColumnFilters/MultiSelectColumnFilter.js.map +1 -1
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js +26 -18
- package/dist/DataTable/ColumnFilters/SortableFilterHeader.js.map +1 -1
- package/dist/DataTable/EmptyState.js +8 -3
- package/dist/DataTable/EmptyState.js.map +1 -1
- package/dist/DataTable/FilterChips.js +11 -5
- package/dist/DataTable/FilterChips.js.map +1 -1
- package/dist/DataTable/GoToPage.js +9 -4
- package/dist/DataTable/GoToPage.js.map +1 -1
- package/dist/DataTable/ServerTable.js +74 -61
- package/dist/DataTable/ServerTable.js.map +1 -1
- package/dist/DataTable/columns.js +1 -8
- package/dist/DataTable/columns.js.map +1 -1
- package/dist/access/PermissionMatrix.js +18 -9
- package/dist/access/PermissionMatrix.js.map +1 -1
- package/dist/account/AccountSecuritySection.js +8 -3
- package/dist/account/AccountSecuritySection.js.map +1 -1
- package/dist/account/DemoTotpCodeButton.js +8 -3
- package/dist/account/DemoTotpCodeButton.js.map +1 -1
- package/dist/account/ProfileScreen.js +19 -13
- package/dist/account/ProfileScreen.js.map +1 -1
- package/dist/account/SignInScreen.js +6 -2
- package/dist/account/SignInScreen.js.map +1 -1
- package/dist/appShell/AccountMenu.js +13 -5
- package/dist/appShell/AccountMenu.js.map +1 -1
- package/dist/appShell/AppLayout.js +100 -71
- package/dist/appShell/AppLayout.js.map +1 -1
- package/dist/appShell/AppearanceSettings.js +21 -13
- package/dist/appShell/AppearanceSettings.js.map +1 -1
- package/dist/appShell/AuthLayout.js +26 -13
- package/dist/appShell/AuthLayout.js.map +1 -1
- package/dist/appShell/BarActions.js +10 -5
- package/dist/appShell/BarActions.js.map +1 -1
- package/dist/appShell/BarCapsule.js +9 -4
- package/dist/appShell/BarCapsule.js.map +1 -1
- package/dist/appShell/BarSearch.js +7 -3
- package/dist/appShell/BarSearch.js.map +1 -1
- package/dist/appShell/HeaderTools.js +20 -15
- package/dist/appShell/HeaderTools.js.map +1 -1
- package/dist/appShell/MobileTabBar.js +25 -14
- package/dist/appShell/MobileTabBar.js.map +1 -1
- package/dist/appShell/SettingsMenu.js +7 -3
- package/dist/appShell/SettingsMenu.js.map +1 -1
- package/dist/appShell/SideMenu.js +53 -35
- package/dist/appShell/SideMenu.js.map +1 -1
- package/dist/appShell/SideMenuEntry.js +18 -9
- package/dist/appShell/SideMenuEntry.js.map +1 -1
- package/dist/appShell/SubmenuActionsMenu.js +6 -2
- package/dist/appShell/SubmenuActionsMenu.js.map +1 -1
- package/dist/appShell/TabBarEditor.js +16 -7
- package/dist/appShell/TabBarEditor.js.map +1 -1
- package/dist/appShell/TopMenu.js +12 -7
- package/dist/appShell/TopMenu.js.map +1 -1
- package/dist/appShell/Wordmark.js +6 -2
- package/dist/appShell/Wordmark.js.map +1 -1
- package/dist/auth/PasswordChecklist.js +14 -7
- package/dist/auth/PasswordChecklist.js.map +1 -1
- package/dist/calendar/EventCalendar.js +26 -19
- package/dist/calendar/EventCalendar.js.map +1 -1
- package/dist/calendar/EventDetails.js +28 -13
- package/dist/calendar/EventDetails.js.map +1 -1
- package/dist/downloads/DownloadManager.js +34 -18
- package/dist/downloads/DownloadManager.js.map +1 -1
- package/dist/feedback/ConfirmDialog.js +22 -17
- package/dist/feedback/ConfirmDialog.js.map +1 -1
- package/dist/feedback/ModalWaiting.js +12 -5
- package/dist/feedback/ModalWaiting.js.map +1 -1
- package/dist/feedback/NewBuildNotice.js +16 -8
- package/dist/feedback/NewBuildNotice.js.map +1 -1
- package/dist/filepond/FileDropzone.js +39 -31
- package/dist/filepond/FileDropzone.js.map +1 -1
- package/dist/forms/FormProblemAlert.js +4 -1
- package/dist/forms/FormProblemAlert.js.map +1 -1
- package/dist/forms/auth/SignInForm.js +6 -2
- package/dist/forms/auth/SignInForm.js.map +1 -1
- package/dist/forms/security/SecondStepForm.js +6 -2
- package/dist/forms/security/SecondStepForm.js.map +1 -1
- package/dist/forms/security/TwoStepVerificationCard.js +19 -9
- package/dist/forms/security/TwoStepVerificationCard.js.map +1 -1
- package/dist/hookForm/FormProblemAlert.js +4 -1
- package/dist/hookForm/FormProblemAlert.js.map +1 -1
- package/dist/inbox/InboxLayout.js +47 -29
- package/dist/inbox/InboxLayout.js.map +1 -1
- package/dist/inbox/MessageList.js +53 -32
- package/dist/inbox/MessageList.js.map +1 -1
- package/dist/inbox/MessageView.js +31 -15
- package/dist/inbox/MessageView.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/inputs/ColorInput.js +22 -17
- package/dist/inputs/ColorInput.js.map +1 -1
- package/dist/inputs/FormSection.js +64 -64
- package/dist/inputs/FormSection.js.map +1 -1
- package/dist/inputs/SelectInput.js +21 -16
- package/dist/inputs/SelectInput.js.map +1 -1
- package/dist/inputs/TextInput.js +25 -20
- package/dist/inputs/TextInput.js.map +1 -1
- package/dist/inputs/WizardLayout.js +21 -15
- package/dist/inputs/WizardLayout.js.map +1 -1
- package/dist/kanban/KanbanBoard.js +82 -50
- package/dist/kanban/KanbanBoard.js.map +1 -1
- package/dist/maps/MapView.js +19 -14
- package/dist/maps/MapView.js.map +1 -1
- package/dist/pwa/InstallAppSetting.js +4 -1
- package/dist/pwa/InstallAppSetting.js.map +1 -1
- package/dist/pwa/InstallInstructionsDialog.js +6 -2
- package/dist/pwa/InstallInstructionsDialog.js.map +1 -1
- package/dist/pwa/InstallPromptBanner.js +10 -5
- package/dist/pwa/InstallPromptBanner.js.map +1 -1
- package/dist/security/ActivityLog.js +6 -2
- package/dist/security/ActivityLog.js.map +1 -1
- package/dist/security/RecoveryCodesDialog.js +8 -3
- package/dist/security/RecoveryCodesDialog.js.map +1 -1
- package/dist/security/SessionsCard.js +12 -5
- package/dist/security/SessionsCard.js.map +1 -1
- package/dist/theme/components/glassComponents.js +19 -16
- package/dist/theme/components/glassComponents.js.map +1 -1
- package/dist/theme/components/minimalComponents.js +6 -2
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/theme/createToolkitTheme.js +8 -1
- package/dist/theme/createToolkitTheme.js.map +1 -1
- package/dist/theme/cssTokens.js +23 -0
- package/dist/theme/cssTokens.js.map +1 -0
- package/dist/theme/glassSurface.js +9 -3
- package/dist/theme/glassSurface.js.map +1 -1
- package/dist/theme/glassToasterStyles.js +2 -3
- package/dist/theme/glassToasterStyles.js.map +1 -1
- package/dist/theme/themePieces.js +16 -0
- package/dist/theme/themePieces.js.map +1 -0
- package/dist/types/DataTable/AuditTrailDialog.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/HeaderFilterPopover.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/MultiSelectColumnFilter.d.ts +1 -1
- package/dist/types/DataTable/ColumnFilters/SortableFilterHeader.d.ts +1 -1
- package/dist/types/DataTable/EmptyState.d.ts +1 -1
- package/dist/types/DataTable/FilterChips.d.ts +1 -1
- package/dist/types/DataTable/GoToPage.d.ts +1 -1
- package/dist/types/DataTable/ServerTable.d.ts +1 -1
- package/dist/types/access/PermissionMatrix.d.ts +1 -1
- package/dist/types/account/AccountSecuritySection.d.ts +1 -1
- package/dist/types/account/DemoTotpCodeButton.d.ts +1 -1
- package/dist/types/account/ProfileScreen.d.ts +1 -1
- package/dist/types/account/SignInScreen.d.ts +1 -1
- package/dist/types/appShell/AccountMenu.d.ts +1 -1
- package/dist/types/appShell/AppLayout.d.ts +1 -1
- package/dist/types/appShell/AppearanceSettings.d.ts +1 -1
- package/dist/types/appShell/AuthLayout.d.ts +1 -1
- package/dist/types/appShell/BarActions.d.ts +1 -1
- package/dist/types/appShell/BarCapsule.d.ts +1 -1
- package/dist/types/appShell/BarSearch.d.ts +1 -1
- package/dist/types/appShell/HeaderTools.d.ts +1 -1
- package/dist/types/appShell/MobileTabBar.d.ts +1 -1
- package/dist/types/appShell/SettingsMenu.d.ts +1 -1
- package/dist/types/appShell/SideMenu.d.ts +1 -1
- package/dist/types/appShell/SideMenuEntry.d.ts +1 -1
- package/dist/types/appShell/SubmenuActionsMenu.d.ts +1 -1
- package/dist/types/appShell/TabBarEditor.d.ts +1 -1
- package/dist/types/appShell/TopMenu.d.ts +1 -1
- package/dist/types/appShell/Wordmark.d.ts +1 -1
- package/dist/types/auth/PasswordChecklist.d.ts +1 -1
- package/dist/types/calendar/EventCalendar.d.ts +1 -1
- package/dist/types/calendar/EventDetails.d.ts +1 -1
- package/dist/types/feedback/ConfirmDialog.d.ts +1 -1
- package/dist/types/feedback/ModalWaiting.d.ts +1 -1
- package/dist/types/feedback/NewBuildNotice.d.ts +1 -1
- package/dist/types/filepond/FileDropzone.d.ts +1 -1
- package/dist/types/forms/auth/SignInForm.d.ts +1 -1
- package/dist/types/forms/security/SecondStepForm.d.ts +1 -1
- package/dist/types/forms/security/TwoStepVerificationCard.d.ts +1 -1
- package/dist/types/inbox/InboxLayout.d.ts +1 -1
- package/dist/types/inbox/MessageList.d.ts +1 -1
- package/dist/types/inbox/MessageView.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/inputs/ColorInput.d.ts +1 -1
- package/dist/types/inputs/FormSection.d.ts +2 -2
- package/dist/types/inputs/SelectInput.d.ts +1 -1
- package/dist/types/inputs/TextInput.d.ts +1 -1
- package/dist/types/inputs/WizardLayout.d.ts +1 -1
- package/dist/types/kanban/KanbanBoard.d.ts +1 -1
- package/dist/types/maps/MapView.d.ts +1 -1
- package/dist/types/pwa/InstallInstructionsDialog.d.ts +1 -1
- package/dist/types/pwa/InstallPromptBanner.d.ts +1 -1
- package/dist/types/security/ActivityLog.d.ts +1 -1
- package/dist/types/security/RecoveryCodesDialog.d.ts +1 -1
- package/dist/types/security/SessionsCard.d.ts +1 -1
- package/dist/types/theme/createToolkitTheme.d.ts +12 -1
- package/dist/types/theme/cssTokens.d.ts +46 -0
- package/dist/types/theme/glassSurface.d.ts +3 -0
- package/dist/types/theme/themePieces.d.ts +20 -0
- package/dist/types/ui/actions/ActionsMenu.d.ts +1 -1
- package/dist/types/ui/actions/DownloadMenu.d.ts +1 -1
- package/dist/types/ui/actions/SelectionActions.d.ts +1 -1
- package/dist/types/ui/filters/DateRangeFilter.d.ts +1 -1
- package/dist/types/ui/filters/PeriodRangeSelector.d.ts +1 -1
- package/dist/types/ui/filters/QuickFilters.d.ts +1 -1
- package/dist/types/ui/filters/SegmentedControl.d.ts +7 -2
- package/dist/types/ui/labels/StatusChip.d.ts +1 -1
- package/dist/types/ui/labels/Tag.d.ts +1 -1
- package/dist/types/ui/layout/CollapsibleSection.d.ts +1 -1
- package/dist/types/ui/layout/ContentDialog.d.ts +1 -1
- package/dist/types/ui/layout/DefinitionList.d.ts +1 -1
- package/dist/types/ui/layout/DetailHeader.d.ts +1 -1
- package/dist/types/ui/layout/ErrorPage.d.ts +1 -1
- package/dist/types/ui/layout/GlassPanel.d.ts +1 -1
- package/dist/types/ui/layout/PageHeader.d.ts +1 -1
- package/dist/types/ui/layout/PageHeading.d.ts +1 -1
- package/dist/types/ui/layout/ProfileHeader.d.ts +1 -1
- package/dist/types/ui/layout/ScrollableRow.d.ts +1 -1
- package/dist/types/ui/layout/SectionCard.d.ts +1 -1
- package/dist/types/ui/metrics/BarList.d.ts +1 -1
- package/dist/types/ui/metrics/CalendarHeatmap.d.ts +1 -1
- package/dist/types/ui/metrics/ChartTooltip.d.ts +1 -1
- package/dist/types/ui/metrics/ColumnChart.d.ts +1 -1
- package/dist/types/ui/metrics/LineChart.d.ts +1 -1
- package/dist/types/ui/metrics/MetricCard.d.ts +1 -1
- package/dist/types/ui/metrics/TableToggle.d.ts +1 -1
- package/dist/types/ui/navigation/ColorSchemeToggle.d.ts +1 -1
- package/dist/types/ui/navigation/FontSizeToggle.d.ts +1 -1
- package/dist/types/ui/navigation/LanguageToggle.d.ts +1 -1
- package/dist/types/ui/navigation/PageBreadcrumbs.d.ts +1 -1
- package/dist/types/ui/timeline/GroupedTimeline.d.ts +1 -1
- package/dist/types/upload/FileDropzone.d.ts +1 -1
- package/dist/ui/actions/ActionsMenu.js +9 -4
- package/dist/ui/actions/ActionsMenu.js.map +1 -1
- package/dist/ui/actions/DownloadMenu.js +21 -17
- package/dist/ui/actions/DownloadMenu.js.map +1 -1
- package/dist/ui/actions/SelectionActions.js +15 -7
- package/dist/ui/actions/SelectionActions.js.map +1 -1
- package/dist/ui/filters/DateRangeFilter.js +11 -5
- package/dist/ui/filters/DateRangeFilter.js.map +1 -1
- package/dist/ui/filters/PeriodRangeSelector.js +11 -5
- package/dist/ui/filters/PeriodRangeSelector.js.map +1 -1
- package/dist/ui/filters/QuickFilters.js +16 -8
- package/dist/ui/filters/QuickFilters.js.map +1 -1
- package/dist/ui/filters/SegmentedControl.js +12 -5
- package/dist/ui/filters/SegmentedControl.js.map +1 -1
- package/dist/ui/labels/StatusChip.js +35 -25
- package/dist/ui/labels/StatusChip.js.map +1 -1
- package/dist/ui/labels/Tag.js +8 -3
- package/dist/ui/labels/Tag.js.map +1 -1
- package/dist/ui/layout/CollapsibleSection.js +18 -8
- package/dist/ui/layout/CollapsibleSection.js.map +1 -1
- package/dist/ui/layout/ContentDialog.js +13 -6
- package/dist/ui/layout/ContentDialog.js.map +1 -1
- package/dist/ui/layout/DefinitionList.js +13 -6
- package/dist/ui/layout/DefinitionList.js.map +1 -1
- package/dist/ui/layout/DetailHeader.js +14 -6
- package/dist/ui/layout/DetailHeader.js.map +1 -1
- package/dist/ui/layout/ErrorPage.js +20 -15
- package/dist/ui/layout/ErrorPage.js.map +1 -1
- package/dist/ui/layout/FullPageLoader.js +4 -1
- package/dist/ui/layout/FullPageLoader.js.map +1 -1
- package/dist/ui/layout/GlassPanel.js +8 -4
- package/dist/ui/layout/GlassPanel.js.map +1 -1
- package/dist/ui/layout/PageHeader.js +10 -4
- package/dist/ui/layout/PageHeader.js.map +1 -1
- package/dist/ui/layout/PageHeading.js +17 -8
- package/dist/ui/layout/PageHeading.js.map +1 -1
- package/dist/ui/layout/ProfileHeader.js +21 -10
- package/dist/ui/layout/ProfileHeader.js.map +1 -1
- package/dist/ui/layout/ScrollableRow.js +19 -15
- package/dist/ui/layout/ScrollableRow.js.map +1 -1
- package/dist/ui/layout/SectionCard.js +10 -4
- package/dist/ui/layout/SectionCard.js.map +1 -1
- package/dist/ui/metrics/BarList.js +9 -4
- package/dist/ui/metrics/BarList.js.map +1 -1
- package/dist/ui/metrics/CalendarHeatmap.js +40 -21
- package/dist/ui/metrics/CalendarHeatmap.js.map +1 -1
- package/dist/ui/metrics/ChartTooltip.js +6 -2
- package/dist/ui/metrics/ChartTooltip.js.map +1 -1
- package/dist/ui/metrics/ColumnChart.js +22 -18
- package/dist/ui/metrics/ColumnChart.js.map +1 -1
- package/dist/ui/metrics/LineChart.js +20 -16
- package/dist/ui/metrics/LineChart.js.map +1 -1
- package/dist/ui/metrics/MetricCard.js +42 -24
- package/dist/ui/metrics/MetricCard.js.map +1 -1
- package/dist/ui/metrics/TableToggle.js +7 -2
- package/dist/ui/metrics/TableToggle.js.map +1 -1
- package/dist/ui/metrics/chartParts.js +14 -5
- package/dist/ui/metrics/chartParts.js.map +1 -1
- package/dist/ui/navigation/ColorSchemeToggle.js +8 -4
- package/dist/ui/navigation/ColorSchemeToggle.js.map +1 -1
- package/dist/ui/navigation/FontSizeToggle.js +6 -2
- package/dist/ui/navigation/FontSizeToggle.js.map +1 -1
- package/dist/ui/navigation/LanguageToggle.js +8 -4
- package/dist/ui/navigation/LanguageToggle.js.map +1 -1
- package/dist/ui/navigation/PageBreadcrumbs.js +12 -6
- package/dist/ui/navigation/PageBreadcrumbs.js.map +1 -1
- package/dist/ui/timeline/GroupedTimeline.js +26 -13
- package/dist/ui/timeline/GroupedTimeline.js.map +1 -1
- package/dist/upload/FileDropzone.js +35 -25
- package/dist/upload/FileDropzone.js.map +1 -1
- package/package.json +1 -1
|
@@ -10,6 +10,15 @@ import TableRow from "@mui/material/TableRow";
|
|
|
10
10
|
import Typography from "@mui/material/Typography";
|
|
11
11
|
import { useTheme } from "@mui/material/styles";
|
|
12
12
|
import { paletteOf } from "../../theme/components/paletteOf.js";
|
|
13
|
+
import { piecePart } from "../../theme/themePieces.js";
|
|
14
|
+
const THEME_NAME = "CalcitaChart";
|
|
15
|
+
const ChartTableArea = piecePart(THEME_NAME, "TableArea", Box);
|
|
16
|
+
const ChartLegendPart = piecePart(THEME_NAME, "Legend", Box);
|
|
17
|
+
const ChartLegendItem = piecePart(THEME_NAME, "LegendItem", Box);
|
|
18
|
+
const ChartLegendLine = piecePart(THEME_NAME, "LegendLine", Box);
|
|
19
|
+
const ChartLegendSwatch = piecePart(THEME_NAME, "LegendSwatch", Box);
|
|
20
|
+
const ChartReference = piecePart(THEME_NAME, "Reference", Box);
|
|
21
|
+
const ChartReferenceLine = piecePart(THEME_NAME, "ReferenceLine", Box);
|
|
13
22
|
const AXIS_FONT_PIXELS = 12;
|
|
14
23
|
const AXIS_MINIMUM_WIDTH_PIXELS = 48;
|
|
15
24
|
const AXIS_PADDING_PIXELS = 16;
|
|
@@ -50,7 +59,7 @@ function highestValueOf(data, series, referenceLine) {
|
|
|
50
59
|
return Math.max(0, ...values, referenceLine?.value ?? 0);
|
|
51
60
|
}
|
|
52
61
|
function ChartTable({ data, series, periodLabel, formatValue, height }) {
|
|
53
|
-
return /* @__PURE__ */ jsx(
|
|
62
|
+
return /* @__PURE__ */ jsx(ChartTableArea, { ownSx: { maxHeight: height, overflowY: "auto" }, children: /* @__PURE__ */ jsxs(Table, { size: "small", children: [
|
|
54
63
|
/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
55
64
|
/* @__PURE__ */ jsx(TableCell, { children: periodLabel }),
|
|
56
65
|
series.map((item) => /* @__PURE__ */ jsx(TableCell, { align: "right", children: item.label }, item.key))
|
|
@@ -65,8 +74,8 @@ function ChartLegend({ series, showsDash = false, ariaLabel }) {
|
|
|
65
74
|
if (series.length < 2) {
|
|
66
75
|
return null;
|
|
67
76
|
}
|
|
68
|
-
return /* @__PURE__ */ jsx(
|
|
69
|
-
showsDash ? /* @__PURE__ */ jsx(
|
|
77
|
+
return /* @__PURE__ */ jsx(ChartLegendPart, { component: "ul", "aria-label": ariaLabel, ownSx: { display: "flex", flexWrap: "wrap", gap: 2, listStyle: "none", p: 0, m: 0, mt: 1 }, children: series.map((item) => /* @__PURE__ */ jsxs(ChartLegendItem, { component: "li", ownSx: { display: "flex", alignItems: "center", gap: 0.75 }, children: [
|
|
78
|
+
showsDash ? /* @__PURE__ */ jsx(ChartLegendLine, { component: "svg", "aria-hidden": "true", width: SWATCH_PIXELS, height: 8, ownSx: { color: item.color }, children: /* @__PURE__ */ jsx("line", { x1: "0", y1: "4", x2: SWATCH_PIXELS, y2: "4", stroke: "currentColor", strokeWidth: "2", strokeDasharray: item.dash }) }) : /* @__PURE__ */ jsx(ChartLegendSwatch, { "aria-hidden": "true", ownSx: { width: 10, height: 10, borderRadius: 0.5, bgcolor: item.color } }),
|
|
70
79
|
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: item.label })
|
|
71
80
|
] }, item.key)) });
|
|
72
81
|
}
|
|
@@ -74,8 +83,8 @@ function ReferenceCaption({ referenceLine }) {
|
|
|
74
83
|
if (!referenceLine) {
|
|
75
84
|
return null;
|
|
76
85
|
}
|
|
77
|
-
return /* @__PURE__ */ jsxs(
|
|
78
|
-
/* @__PURE__ */ jsx(
|
|
86
|
+
return /* @__PURE__ */ jsxs(ChartReference, { ownSx: { display: "flex", alignItems: "center", justifyContent: "flex-end", gap: 0.75, mt: 0.5 }, children: [
|
|
87
|
+
/* @__PURE__ */ jsx(ChartReferenceLine, { "aria-hidden": "true", ownSx: { width: SWATCH_PIXELS, borderTop: "2px dashed", borderColor: "text.secondary" } }),
|
|
79
88
|
/* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", children: referenceLine.label })
|
|
80
89
|
] });
|
|
81
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chartParts.js","sources":["../../../src/ui/metrics/chartParts.jsx"],"sourcesContent":["// Lo que comparten las gráficas de tiempo de la casa (ColumnChart y LineChart): las medidas del eje de valores, la vista de\n// tabla, la leyenda de las series y el rótulo de la línea de referencia. Un solo lugar: si cambia cómo se lee una tabla o una\n// leyenda, cambia en las dos gráficas.\n//\n// Las reglas de la guía de Apple (HIG «Charts» y «Charting data»):\n// - con una serie, sin leyenda (el título ya dice qué es); con varias, leyenda;\n// - el color no va solo: cada serie de líneas lleva además su trazo (continuo, rayado, punteado) y toda gráfica tiene su\n// vista de tabla;\n// - ejes y rejilla tenues, que den contexto sin competir con los datos.\n//\n// Los colores salen del tema (useChartColors), nunca escritos como var(--mui-…): un proyecto con su propio prefijo de\n// variables (cssVarPrefix) o sin variables CSS se quedaba con ejes y texto sin color.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useMemo} from 'react';\nimport Box from '@mui/material/Box';\nimport Table from '@mui/material/Table';\nimport TableBody from '@mui/material/TableBody';\nimport TableCell from '@mui/material/TableCell';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\n\nconst AXIS_FONT_PIXELS = 12;\n// ancho del eje de valores: nunca menos que esto, más lo que pida su número más largo\nconst AXIS_MINIMUM_WIDTH_PIXELS = 48;\nconst AXIS_PADDING_PIXELS = 16;\n// ancho aproximado de un carácter del eje a 12 px\nconst AXIS_CHARACTER_PIXELS = 7;\n// la marca más alta del eje suele pasar del valor más alto y puede llevar un carácter más (999,999 → 1,000,000)\nconst AXIS_EXTRA_CHARACTERS = 1;\n// largo de la muestra que acompaña al rótulo de la línea de referencia y a cada serie de la leyenda\nconst SWATCH_PIXELS = 20;\n\n/** Los colores de las series, en orden, cuando la pantalla no dice el suyo: los del tema. */\nexport const CHART_SERIES_TONES = Object.freeze(['primary', 'info', 'success', 'warning', 'error', 'secondary']);\n/** El trazo de cada serie de líneas, en orden (continuo, rayado, punteado): así no se distinguen solo por el color. */\nexport const CHART_LINE_DASHES = Object.freeze(['0', '6 4', '2 3']);\n\n/**\n * Los colores de las gráficas, leídos del tema: con variables CSS salen como var(--su-prefijo-palette-…) y sirven en claro y\n * en oscuro sin volver a pintar; sin ellas, el color de la paleta.\n *\n * @returns {{palette: object, axis: string, axisText: {fill: string, fontSize: number}, grid: string, text: string, muted: string, hover: string}}\n */\nexport function useChartColors() {\n const palette = paletteOf(useTheme());\n return useMemo(() => ({\n palette,\n axis: palette.text.secondary,\n axisText: {fill: palette.text.secondary, fontSize: AXIS_FONT_PIXELS},\n grid: palette.divider,\n text: palette.text.primary,\n muted: palette.text.secondary,\n hover: palette.action.hover,\n }), [palette]);\n}\n\n/**\n * Ancho del eje de valores para que quepa su número más largo.\n *\n * @param {number} highestValue el valor más alto que se dibuja\n * @param {(value: number) => string} formatValue cómo se escribe un valor\n * @returns {number} pixeles\n */\nexport function valueAxisWidthOf(highestValue, formatValue) {\n const longestLabel = formatValue(highestValue).length + AXIS_EXTRA_CHARACTERS;\n return Math.max(AXIS_MINIMUM_WIDTH_PIXELS, AXIS_PADDING_PIXELS + longestLabel * AXIS_CHARACTER_PIXELS);\n}\n\n/**\n * Las series con su color y su trazo resueltos (lo que la pantalla no dijo, por su lugar en la lista).\n *\n * @param {Array<{key: string, label: string, tone?: string, dash?: string}>} series\n * @param {object} palette la del tema (useChartColors().palette)\n * @returns {Array<{key: string, label: string, tone: string, color: string, dash: string}>}\n */\nexport function resolvedSeriesOf(series, palette) {\n return series.map((item, index) => {\n const tone = item.tone ?? CHART_SERIES_TONES[index % CHART_SERIES_TONES.length];\n return {\n ...item,\n tone,\n color: (palette[tone] ?? palette.primary).main,\n dash: item.dash ?? CHART_LINE_DASHES[index % CHART_LINE_DASHES.length],\n };\n });\n}\n\n/**\n * El valor más alto de todas las series (y de la línea de referencia), para el ancho del eje.\n *\n * @param {Array<Record<string, any>>} data\n * @param {Array<{key: string}>} series\n * @param {{value: number}|undefined} referenceLine\n * @returns {number}\n */\nexport function highestValueOf(data, series, referenceLine) {\n const values = data.flatMap((row) => series.map((item) => Number(row[item.key]) || 0));\n return Math.max(0, ...values, referenceLine?.value ?? 0);\n}\n\n/**\n * La vista de tabla: un renglón por periodo y una columna por serie.\n *\n * @param {object} props\n * @param {Array<Record<string, any>>} props.data\n * @param {Array<{key: string, label: string}>} props.series\n * @param {string} props.periodLabel\n * @param {(value: number) => string} props.formatValue\n * @param {number} props.height\n */\nexport function ChartTable({data, series, periodLabel, formatValue, height}) {\n return (\n <Box sx={{maxHeight: height, overflowY: 'auto'}}>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{periodLabel}</TableCell>\n {series.map((item) => <TableCell key={item.key} align=\"right\">{item.label}</TableCell>)}\n </TableRow>\n </TableHead>\n <TableBody>\n {data.map((row) => (\n <TableRow key={row.key}>\n <TableCell>{row.label}</TableCell>\n {series.map((item) => <TableCell key={item.key} align=\"right\">{formatValue(row[item.key])}</TableCell>)}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </Box>\n );\n}\n\n/**\n * La leyenda: solo con dos series o más. Cada una con su muestra (su color y, en líneas, su trazo).\n *\n * @param {object} props\n * @param {Array<{key: string, label: string, color: string, dash: string}>} props.series ya resueltas\n * @param {boolean} [props.showsDash] la muestra con el trazo de su línea (LineChart); sin él, un cuadro de color\n * @param {string} props.ariaLabel\n */\nexport function ChartLegend({series, showsDash = false, ariaLabel}) {\n if (series.length < 2) {\n return null;\n }\n return (\n <Box component=\"ul\" aria-label={ariaLabel} sx={{display: 'flex', flexWrap: 'wrap', gap: 2, listStyle: 'none', p: 0, m: 0, mt: 1}}>\n {series.map((item) => (\n <Box component=\"li\" key={item.key} sx={{display: 'flex', alignItems: 'center', gap: 0.75}}>\n {showsDash ? (\n <Box component=\"svg\" aria-hidden=\"true\" width={SWATCH_PIXELS} height={8} sx={{color: item.color}}>\n <line x1=\"0\" y1=\"4\" x2={SWATCH_PIXELS} y2=\"4\" stroke=\"currentColor\" strokeWidth=\"2\" strokeDasharray={item.dash} />\n </Box>\n ) : (\n <Box aria-hidden=\"true\" sx={{width: 10, height: 10, borderRadius: 0.5, bgcolor: item.color}} />\n )}\n <Typography variant=\"caption\" color=\"text.secondary\">{item.label}</Typography>\n </Box>\n ))}\n </Box>\n );\n}\n\n/**\n * El rótulo de la línea de referencia (la meta), debajo de la gráfica: sobre la línea se encimaba con la última columna, y\n * así también lo lee quien ve la tabla.\n *\n * @param {{referenceLine?: {value: number, label: string}}} props\n */\nexport function ReferenceCaption({referenceLine}) {\n if (!referenceLine) {\n return null;\n }\n return (\n <Box sx={{display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 0.75, mt: 0.5}}>\n <Box aria-hidden=\"true\" sx={{width: SWATCH_PIXELS, borderTop: '2px dashed', borderColor: 'text.secondary'}} />\n <Typography variant=\"caption\" color=\"text.secondary\">{referenceLine.label}</Typography>\n </Box>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chartParts.js","sources":["../../../src/ui/metrics/chartParts.jsx"],"sourcesContent":["// Lo que comparten las gráficas de tiempo de la casa (ColumnChart y LineChart): las medidas del eje de valores, la vista de\n// tabla, la leyenda de las series y el rótulo de la línea de referencia. Un solo lugar: si cambia cómo se lee una tabla o una\n// leyenda, cambia en las dos gráficas.\n//\n// Las reglas de la guía de Apple (HIG «Charts» y «Charting data»):\n// - con una serie, sin leyenda (el título ya dice qué es); con varias, leyenda;\n// - el color no va solo: cada serie de líneas lleva además su trazo (continuo, rayado, punteado) y toda gráfica tiene su\n// vista de tabla;\n// - ejes y rejilla tenues, que den contexto sin competir con los datos.\n//\n// Los colores salen del tema (useChartColors), nunca escritos como var(--mui-…): un proyecto con su propio prefijo de\n// variables (cssVarPrefix) o sin variables CSS se quedaba con ejes y texto sin color.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaChart con defaultProps y\n// styleOverrides de sus partes: tableArea, legend, legendItem, legendLine, legendSwatch, reference, referenceLine.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useMemo} from 'react';\nimport Box from '@mui/material/Box';\nimport Table from '@mui/material/Table';\nimport TableBody from '@mui/material/TableBody';\nimport TableCell from '@mui/material/TableCell';\nimport TableHead from '@mui/material/TableHead';\nimport TableRow from '@mui/material/TableRow';\nimport Typography from '@mui/material/Typography';\nimport {useTheme} from '@mui/material/styles';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {piecePart} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaChart)\nconst THEME_NAME = 'CalcitaChart';\nconst ChartTableArea = piecePart(THEME_NAME, 'TableArea', Box);\nconst ChartLegendPart = piecePart(THEME_NAME, 'Legend', Box);\nconst ChartLegendItem = piecePart(THEME_NAME, 'LegendItem', Box);\nconst ChartLegendLine = piecePart(THEME_NAME, 'LegendLine', Box);\nconst ChartLegendSwatch = piecePart(THEME_NAME, 'LegendSwatch', Box);\nconst ChartReference = piecePart(THEME_NAME, 'Reference', Box);\nconst ChartReferenceLine = piecePart(THEME_NAME, 'ReferenceLine', Box);\n\nconst AXIS_FONT_PIXELS = 12;\n// ancho del eje de valores: nunca menos que esto, más lo que pida su número más largo\nconst AXIS_MINIMUM_WIDTH_PIXELS = 48;\nconst AXIS_PADDING_PIXELS = 16;\n// ancho aproximado de un carácter del eje a 12 px\nconst AXIS_CHARACTER_PIXELS = 7;\n// la marca más alta del eje suele pasar del valor más alto y puede llevar un carácter más (999,999 → 1,000,000)\nconst AXIS_EXTRA_CHARACTERS = 1;\n// largo de la muestra que acompaña al rótulo de la línea de referencia y a cada serie de la leyenda\nconst SWATCH_PIXELS = 20;\n\n/** Los colores de las series, en orden, cuando la pantalla no dice el suyo: los del tema. */\nexport const CHART_SERIES_TONES = Object.freeze(['primary', 'info', 'success', 'warning', 'error', 'secondary']);\n/** El trazo de cada serie de líneas, en orden (continuo, rayado, punteado): así no se distinguen solo por el color. */\nexport const CHART_LINE_DASHES = Object.freeze(['0', '6 4', '2 3']);\n\n/**\n * Los colores de las gráficas, leídos del tema: con variables CSS salen como var(--su-prefijo-palette-…) y sirven en claro y\n * en oscuro sin volver a pintar; sin ellas, el color de la paleta.\n *\n * @returns {{palette: object, axis: string, axisText: {fill: string, fontSize: number}, grid: string, text: string, muted: string, hover: string}}\n */\nexport function useChartColors() {\n const palette = paletteOf(useTheme());\n return useMemo(() => ({\n palette,\n axis: palette.text.secondary,\n axisText: {fill: palette.text.secondary, fontSize: AXIS_FONT_PIXELS},\n grid: palette.divider,\n text: palette.text.primary,\n muted: palette.text.secondary,\n hover: palette.action.hover,\n }), [palette]);\n}\n\n/**\n * Ancho del eje de valores para que quepa su número más largo.\n *\n * @param {number} highestValue el valor más alto que se dibuja\n * @param {(value: number) => string} formatValue cómo se escribe un valor\n * @returns {number} pixeles\n */\nexport function valueAxisWidthOf(highestValue, formatValue) {\n const longestLabel = formatValue(highestValue).length + AXIS_EXTRA_CHARACTERS;\n return Math.max(AXIS_MINIMUM_WIDTH_PIXELS, AXIS_PADDING_PIXELS + longestLabel * AXIS_CHARACTER_PIXELS);\n}\n\n/**\n * Las series con su color y su trazo resueltos (lo que la pantalla no dijo, por su lugar en la lista).\n *\n * @param {Array<{key: string, label: string, tone?: string, dash?: string}>} series\n * @param {object} palette la del tema (useChartColors().palette)\n * @returns {Array<{key: string, label: string, tone: string, color: string, dash: string}>}\n */\nexport function resolvedSeriesOf(series, palette) {\n return series.map((item, index) => {\n const tone = item.tone ?? CHART_SERIES_TONES[index % CHART_SERIES_TONES.length];\n return {\n ...item,\n tone,\n color: (palette[tone] ?? palette.primary).main,\n dash: item.dash ?? CHART_LINE_DASHES[index % CHART_LINE_DASHES.length],\n };\n });\n}\n\n/**\n * El valor más alto de todas las series (y de la línea de referencia), para el ancho del eje.\n *\n * @param {Array<Record<string, any>>} data\n * @param {Array<{key: string}>} series\n * @param {{value: number}|undefined} referenceLine\n * @returns {number}\n */\nexport function highestValueOf(data, series, referenceLine) {\n const values = data.flatMap((row) => series.map((item) => Number(row[item.key]) || 0));\n return Math.max(0, ...values, referenceLine?.value ?? 0);\n}\n\n/**\n * La vista de tabla: un renglón por periodo y una columna por serie.\n *\n * @param {object} props\n * @param {Array<Record<string, any>>} props.data\n * @param {Array<{key: string, label: string}>} props.series\n * @param {string} props.periodLabel\n * @param {(value: number) => string} props.formatValue\n * @param {number} props.height\n */\nexport function ChartTable({data, series, periodLabel, formatValue, height}) {\n return (\n <ChartTableArea ownSx={{maxHeight: height, overflowY: 'auto'}}>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{periodLabel}</TableCell>\n {series.map((item) => <TableCell key={item.key} align=\"right\">{item.label}</TableCell>)}\n </TableRow>\n </TableHead>\n <TableBody>\n {data.map((row) => (\n <TableRow key={row.key}>\n <TableCell>{row.label}</TableCell>\n {series.map((item) => <TableCell key={item.key} align=\"right\">{formatValue(row[item.key])}</TableCell>)}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </ChartTableArea>\n );\n}\n\n/**\n * La leyenda: solo con dos series o más. Cada una con su muestra (su color y, en líneas, su trazo).\n *\n * @param {object} props\n * @param {Array<{key: string, label: string, color: string, dash: string}>} props.series ya resueltas\n * @param {boolean} [props.showsDash] la muestra con el trazo de su línea (LineChart); sin él, un cuadro de color\n * @param {string} props.ariaLabel\n */\nexport function ChartLegend({series, showsDash = false, ariaLabel}) {\n if (series.length < 2) {\n return null;\n }\n return (\n <ChartLegendPart component=\"ul\" aria-label={ariaLabel} ownSx={{display: 'flex', flexWrap: 'wrap', gap: 2, listStyle: 'none', p: 0, m: 0, mt: 1}}>\n {series.map((item) => (\n <ChartLegendItem component=\"li\" key={item.key} ownSx={{display: 'flex', alignItems: 'center', gap: 0.75}}>\n {showsDash ? (\n <ChartLegendLine component=\"svg\" aria-hidden=\"true\" width={SWATCH_PIXELS} height={8} ownSx={{color: item.color}}>\n <line x1=\"0\" y1=\"4\" x2={SWATCH_PIXELS} y2=\"4\" stroke=\"currentColor\" strokeWidth=\"2\" strokeDasharray={item.dash} />\n </ChartLegendLine>\n ) : (\n <ChartLegendSwatch aria-hidden=\"true\" ownSx={{width: 10, height: 10, borderRadius: 0.5, bgcolor: item.color}} />\n )}\n <Typography variant=\"caption\" color=\"text.secondary\">{item.label}</Typography>\n </ChartLegendItem>\n ))}\n </ChartLegendPart>\n );\n}\n\n/**\n * El rótulo de la línea de referencia (la meta), debajo de la gráfica: sobre la línea se encimaba con la última columna, y\n * así también lo lee quien ve la tabla.\n *\n * @param {{referenceLine?: {value: number, label: string}}} props\n */\nexport function ReferenceCaption({referenceLine}) {\n if (!referenceLine) {\n return null;\n }\n return (\n <ChartReference ownSx={{display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 0.75, mt: 0.5}}>\n <ChartReferenceLine aria-hidden=\"true\" ownSx={{width: SWATCH_PIXELS, borderTop: '2px dashed', borderColor: 'text.secondary'}} />\n <Typography variant=\"caption\" color=\"text.secondary\">{referenceLine.label}</Typography>\n </ChartReference>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,7 +8,11 @@ import { useColorScheme } from "@mui/material/styles";
|
|
|
8
8
|
import DarkModeIcon from "@mui/icons-material/DarkModeOutlined";
|
|
9
9
|
import LightModeIcon from "@mui/icons-material/LightModeOutlined";
|
|
10
10
|
import { nextColorModeOf } from "./colorMode.js";
|
|
11
|
-
|
|
11
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
12
|
+
const THEME_NAME = "CalcitaColorSchemeToggle";
|
|
13
|
+
const SchemeButton = piecePart(THEME_NAME, "Button", IconButton);
|
|
14
|
+
function ColorSchemeToggle(props) {
|
|
15
|
+
const { size = "small", inverted = false, onChange, texts } = usePieceProps(props, THEME_NAME);
|
|
12
16
|
const { t } = useToolkitTranslation("common", texts);
|
|
13
17
|
const { mode, systemMode, setMode } = useColorScheme();
|
|
14
18
|
const [isMounted, setIsMounted] = useState(false);
|
|
@@ -16,12 +20,12 @@ function ColorSchemeToggle({ size = "small", inverted = false, onChange, texts }
|
|
|
16
20
|
setIsMounted(true);
|
|
17
21
|
}, []);
|
|
18
22
|
if (!isMounted || !mode) {
|
|
19
|
-
return /* @__PURE__ */ jsx(
|
|
23
|
+
return /* @__PURE__ */ jsx(SchemeButton, { size, disabled: true, "aria-hidden": "true", ownSx: { width: 34, height: 34 } });
|
|
20
24
|
}
|
|
21
25
|
const nextMode = nextColorModeOf(mode, systemMode);
|
|
22
26
|
const label = t(`colorScheme.switchTo.${nextMode}`);
|
|
23
27
|
return /* @__PURE__ */ jsx(Tooltip, { title: label, children: /* @__PURE__ */ jsx(
|
|
24
|
-
|
|
28
|
+
SchemeButton,
|
|
25
29
|
{
|
|
26
30
|
size,
|
|
27
31
|
"aria-label": label,
|
|
@@ -29,7 +33,7 @@ function ColorSchemeToggle({ size = "small", inverted = false, onChange, texts }
|
|
|
29
33
|
setMode(nextMode);
|
|
30
34
|
onChange?.(nextMode);
|
|
31
35
|
},
|
|
32
|
-
|
|
36
|
+
ownSx: { color: inverted ? "common.white" : "text.secondary" },
|
|
33
37
|
children: nextMode === "light" ? /* @__PURE__ */ jsx(LightModeIcon, { fontSize: "small" }) : /* @__PURE__ */ jsx(DarkModeIcon, { fontSize: "small" })
|
|
34
38
|
}
|
|
35
39
|
) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorSchemeToggle.js","sources":["../../../src/ui/navigation/ColorSchemeToggle.jsx"],"sourcesContent":["// Interruptor de modo claro / oscuro.\n//\n// Dos estados visibles, no tres: el primer clic fija una preferencia explícita que ya se recuerda; para volver a seguir\n// al equipo está «Automático» en Apariencia (AppearanceSettings, en el menú de la cuenta). Recordar lo hace MUI:\n// setMode escribe en localStorage (con las llaves que el proyecto puso en su ThemeProvider) y un script\n// en el HTML la puede leer antes del primer pintado.\n//\n// Va en la cinta de escritorio, en el cajón del teléfono y en el inicio de sesión.\n//\n// El tooltip sale de i18n.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport {useColorScheme} from '@mui/material/styles';\nimport DarkModeIcon from '@mui/icons-material/DarkModeOutlined';\nimport LightModeIcon from '@mui/icons-material/LightModeOutlined';\n\nimport {nextColorModeOf} from '@/ui/navigation/colorMode.js';\n\n/**\n * @param {object} [props]\n * @param {'small'|'medium'} [props.size]\n * @param {boolean} [props.inverted] ícono blanco para fondos oscuros\n * @param {(mode: 'light'|'dark'|'system') => void} [props.onChange] la persona cambió el modo (para guardarlo en su servidor)\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function ColorSchemeToggle({size = 'small', inverted = false, onChange, texts})
|
|
1
|
+
{"version":3,"file":"ColorSchemeToggle.js","sources":["../../../src/ui/navigation/ColorSchemeToggle.jsx"],"sourcesContent":["// Interruptor de modo claro / oscuro.\n//\n// Dos estados visibles, no tres: el primer clic fija una preferencia explícita que ya se recuerda; para volver a seguir\n// al equipo está «Automático» en Apariencia (AppearanceSettings, en el menú de la cuenta). Recordar lo hace MUI:\n// setMode escribe en localStorage (con las llaves que el proyecto puso en su ThemeProvider) y un script\n// en el HTML la puede leer antes del primer pintado.\n//\n// Va en la cinta de escritorio, en el cajón del teléfono y en el inicio de sesión.\n//\n// El tooltip sale de i18n.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaColorSchemeToggle con defaultProps y\n// styleOverrides de sus partes: button.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport {useColorScheme} from '@mui/material/styles';\nimport DarkModeIcon from '@mui/icons-material/DarkModeOutlined';\nimport LightModeIcon from '@mui/icons-material/LightModeOutlined';\n\nimport {nextColorModeOf} from '@/ui/navigation/colorMode.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaColorSchemeToggle)\nconst THEME_NAME = 'CalcitaColorSchemeToggle';\nconst SchemeButton = piecePart(THEME_NAME, 'Button', IconButton);\n\n/**\n * @param {object} [props]\n * @param {'small'|'medium'} [props.size]\n * @param {boolean} [props.inverted] ícono blanco para fondos oscuros\n * @param {(mode: 'light'|'dark'|'system') => void} [props.onChange] la persona cambió el modo (para guardarlo en su servidor)\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nexport default function ColorSchemeToggle(props) {\n // lo que el proyecto puso en su tema (components.CalcitaColorSchemeToggle.defaultProps) y encima lo que llega aquí\n const {size = 'small', inverted = false, onChange, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('common', texts);\n const {mode, systemMode, setMode} = useColorScheme();\n\n // antes de montar en el cliente mode viene undefined: reservamos el lugar para que la barra no brinque\n const [isMounted, setIsMounted] = useState(false);\n useEffect(() => {\n setIsMounted(true);\n }, []);\n if (!isMounted || !mode) {\n return <SchemeButton size={size} disabled aria-hidden=\"true\" ownSx={{width: 34, height: 34}} />;\n }\n\n const nextMode = nextColorModeOf(mode, systemMode);\n const label = t(`colorScheme.switchTo.${nextMode}`);\n\n return (\n <Tooltip title={label}>\n <SchemeButton size={size} aria-label={label} onClick={() => {\n setMode(nextMode);\n onChange?.(nextMode);\n }}\n ownSx={{color: inverted ? 'common.white' : 'text.secondary'}}>\n {nextMode === 'light' ? <LightModeIcon fontSize=\"small\" /> : <DarkModeIcon fontSize=\"small\" />}\n </SchemeButton>\n </Tooltip>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AA2BA,MAAM,aAAa;AACnB,MAAM,eAAe,UAAU,YAAY,UAAU,UAAU;AAS/D,SAAwB,kBAAkB,OAAO;AAE7C,QAAM,EAAC,OAAO,SAAS,WAAW,OAAO,UAAU,UAAS,cAAc,OAAO,UAAU;AAC3F,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,MAAM,YAAY,QAAA,IAAW,eAAA;AAGpC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,YAAU,MAAM;AACZ,iBAAa,IAAI;AAAA,EACrB,GAAG,CAAA,CAAE;AACL,MAAI,CAAC,aAAa,CAAC,MAAM;AACrB,WAAO,oBAAC,cAAA,EAAa,MAAY,UAAQ,MAAC,eAAY,QAAO,OAAO,EAAC,OAAO,IAAI,QAAQ,KAAE,CAAG;AAAA,EACjG;AAEA,QAAM,WAAW,gBAAgB,MAAM,UAAU;AACjD,QAAM,QAAQ,EAAE,wBAAwB,QAAQ,EAAE;AAElD,SACI,oBAAC,SAAA,EAAQ,OAAO,OACZ,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MAAa;AAAA,MAAY,cAAY;AAAA,MAAO,SAAS,MAAM;AAC5C,gBAAQ,QAAQ;AAChB,mBAAW,QAAQ;AAAA,MACvB;AAAA,MACA,OAAO,EAAC,OAAO,WAAW,iBAAiB,iBAAA;AAAA,MAClD,UAAA,aAAa,UAAU,oBAAC,eAAA,EAAc,UAAS,SAAQ,IAAK,oBAAC,cAAA,EAAa,UAAS,QAAA,CAAQ;AAAA,IAAA;AAAA,EAAA,GAEpG;AAER;"}
|
|
@@ -9,12 +9,16 @@ import MenuItem from "@mui/material/MenuItem";
|
|
|
9
9
|
import Tooltip from "@mui/material/Tooltip";
|
|
10
10
|
import FormatSizeIcon from "@mui/icons-material/FormatSize";
|
|
11
11
|
import { useFontSize } from "./useFontSize.js";
|
|
12
|
-
|
|
12
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
13
|
+
const THEME_NAME = "CalcitaFontSizeToggle";
|
|
14
|
+
const FontSizeButton = piecePart(THEME_NAME, "Button", IconButton);
|
|
15
|
+
function FontSizeToggle(props) {
|
|
16
|
+
const { size = "small", onChange, texts } = usePieceProps(props, THEME_NAME);
|
|
13
17
|
const { t } = useToolkitTranslation("layout", texts);
|
|
14
18
|
const { level, setLevel, isMounted, levels } = useFontSize();
|
|
15
19
|
const [anchorElement, setAnchorElement] = useState(null);
|
|
16
20
|
if (!isMounted) {
|
|
17
|
-
return /* @__PURE__ */ jsx(
|
|
21
|
+
return /* @__PURE__ */ jsx(FontSizeButton, { size, disabled: true, "aria-hidden": "true", ownSx: { width: 34, height: 34 } });
|
|
18
22
|
}
|
|
19
23
|
const chooseLevel = (levelKey) => {
|
|
20
24
|
setLevel(levelKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FontSizeToggle.js","sources":["../../../src/ui/navigation/FontSizeToggle.jsx"],"sourcesContent":["// El botón del tamaño de letra (normal, grande, muy grande), del tema de walk-ot: para quien lee con lentes o en una\n// pantalla chica. Va en la barra de escritorio; en el teléfono vive en el menú de ajustes.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\nimport FormatSizeIcon from '@mui/icons-material/FormatSize';\n\nimport {useFontSize} from '@/ui/navigation/useFontSize.js';\n\n/**\n * @param {object} [props]\n * @param {'small'|'medium'} [props.size]\n * @param {(level: string) => void} [props.onChange] la persona eligió otro tamaño (FONT_SIZE_LEVELS; para guardarlo en su servidor)\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nexport default function FontSizeToggle({size = 'small', onChange, texts})
|
|
1
|
+
{"version":3,"file":"FontSizeToggle.js","sources":["../../../src/ui/navigation/FontSizeToggle.jsx"],"sourcesContent":["// El botón del tamaño de letra (normal, grande, muy grande), del tema de walk-ot: para quien lee con lentes o en una\n// pantalla chica. Va en la barra de escritorio; en el teléfono vive en el menú de ajustes.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFontSizeToggle con defaultProps y\n// styleOverrides de sus partes: button.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport IconButton from '@mui/material/IconButton';\nimport ListItemText from '@mui/material/ListItemText';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\nimport Tooltip from '@mui/material/Tooltip';\nimport FormatSizeIcon from '@mui/icons-material/FormatSize';\n\nimport {useFontSize} from '@/ui/navigation/useFontSize.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFontSizeToggle)\nconst THEME_NAME = 'CalcitaFontSizeToggle';\nconst FontSizeButton = piecePart(THEME_NAME, 'Button', IconButton);\n\n/**\n * @param {object} [props]\n * @param {'small'|'medium'} [props.size]\n * @param {(level: string) => void} [props.onChange] la persona eligió otro tamaño (FONT_SIZE_LEVELS; para guardarlo en su servidor)\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nexport default function FontSizeToggle(props) {\n // lo que el proyecto puso en su tema (components.CalcitaFontSizeToggle.defaultProps) y encima lo que llega aquí\n const {size = 'small', onChange, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('layout', texts);\n const {level, setLevel, isMounted, levels} = useFontSize();\n const [anchorElement, setAnchorElement] = useState(null);\n\n // antes de montar no se sabe el nivel guardado: se reserva el lugar para que la barra no brinque\n if (!isMounted) {\n return <FontSizeButton size={size} disabled aria-hidden=\"true\" ownSx={{width: 34, height: 34}} />;\n }\n\n const chooseLevel = (levelKey) => {\n setLevel(levelKey);\n setAnchorElement(null);\n onChange?.(levelKey);\n };\n\n return (\n <>\n <Tooltip title={t('fontSize.label')}>\n <IconButton size={size} aria-label={t('fontSize.label')} aria-haspopup=\"menu\" onClick={(event) => setAnchorElement(event.currentTarget)}>\n <FormatSizeIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n <Menu anchorEl={anchorElement} open={Boolean(anchorElement)} onClose={() => setAnchorElement(null)}\n slotProps={{paper: {sx: {minWidth: 200}}}}>\n {levels.map((option) => (\n <MenuItem key={option.key} selected={option.key === level} onClick={() => chooseLevel(option.key)}>\n <ListItemText primary={t(option.labelKey)} slotProps={{primary: {sx: {fontWeight: option.key === level ? 600 : 400}}}} />\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAoBA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,UAAU,UAAU;AAQjE,SAAwB,eAAe,OAAO;AAE1C,QAAM,EAAC,OAAO,SAAS,UAAU,UAAS,cAAc,OAAO,UAAU;AACzE,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,OAAO,UAAU,WAAW,OAAA,IAAU,YAAA;AAC7C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,IAAI;AAGvD,MAAI,CAAC,WAAW;AACZ,WAAO,oBAAC,gBAAA,EAAe,MAAY,UAAQ,MAAC,eAAY,QAAO,OAAO,EAAC,OAAO,IAAI,QAAQ,KAAE,CAAG;AAAA,EACnG;AAEA,QAAM,cAAc,CAAC,aAAa;AAC9B,aAAS,QAAQ;AACjB,qBAAiB,IAAI;AACrB,eAAW,QAAQ;AAAA,EACvB;AAEA,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA,oBAAC,SAAA,EAAQ,OAAO,EAAE,gBAAgB,GAC9B,UAAA,oBAAC,YAAA,EAAW,MAAY,cAAY,EAAE,gBAAgB,GAAG,iBAAc,QAAO,SAAS,CAAC,UAAU,iBAAiB,MAAM,aAAa,GAClI,UAAA,oBAAC,gBAAA,EAAe,UAAS,QAAA,CAAQ,EAAA,CACrC,EAAA,CACJ;AAAA,IACA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAK,UAAU;AAAA,QAAe,MAAM,QAAQ,aAAa;AAAA,QAAG,SAAS,MAAM,iBAAiB,IAAI;AAAA,QAC3F,WAAW,EAAC,OAAO,EAAC,IAAI,EAAC,UAAU,IAAA,IAAI;AAAA,QACxC,iBAAO,IAAI,CAAC,WACT,oBAAC,UAAA,EAA0B,UAAU,OAAO,QAAQ,OAAO,SAAS,MAAM,YAAY,OAAO,GAAG,GAC5F,UAAA,oBAAC,gBAAa,SAAS,EAAE,OAAO,QAAQ,GAAG,WAAW,EAAC,SAAS,EAAC,IAAI,EAAC,YAAY,OAAO,QAAQ,QAAQ,MAAM,MAAG,EAAC,GAAI,EAAA,GAD5G,OAAO,GAEtB,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EACL,GACJ;AAER;"}
|
|
@@ -7,8 +7,12 @@ import MenuItem from "@mui/material/MenuItem";
|
|
|
7
7
|
import { useToolkitSettings } from "../../toolkit/toolkitContext.js";
|
|
8
8
|
import { DEFAULT_LANGUAGES } from "../../toolkit/toolkitSettings.js";
|
|
9
9
|
import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
|
|
10
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
11
|
+
const THEME_NAME = "CalcitaLanguageToggle";
|
|
12
|
+
const LanguageButton = piecePart(THEME_NAME, "Button", Button);
|
|
10
13
|
const LANGUAGES = Object.freeze(DEFAULT_LANGUAGES.map((entry) => entry.code));
|
|
11
|
-
function LanguageToggle(
|
|
14
|
+
function LanguageToggle(props) {
|
|
15
|
+
const { inverted = false, onChange, texts } = usePieceProps(props, THEME_NAME);
|
|
12
16
|
const { t, language, changeLanguage } = useToolkitTranslation("common", texts);
|
|
13
17
|
const { languages = DEFAULT_LANGUAGES } = useToolkitSettings();
|
|
14
18
|
const [menuAnchor, setMenuAnchor] = useState(null);
|
|
@@ -22,11 +26,11 @@ function LanguageToggle({ inverted = false, onChange, texts }) {
|
|
|
22
26
|
if (languages.length <= 2) {
|
|
23
27
|
const next = languages.find((entry) => entry.code !== current.code) ?? current;
|
|
24
28
|
const ariaLabel = t(`language.switchTo.${next.code}`, { defaultValue: t("language.switchToNamed", { name: next.label }) });
|
|
25
|
-
return /* @__PURE__ */ jsx(
|
|
29
|
+
return /* @__PURE__ */ jsx(LanguageButton, { size: "small", variant: "text", onClick: () => choose(next.code), lang: next.code, "aria-label": ariaLabel, ownSx: buttonSx, children: next.code.toUpperCase() });
|
|
26
30
|
}
|
|
27
31
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
28
32
|
/* @__PURE__ */ jsx(
|
|
29
|
-
|
|
33
|
+
LanguageButton,
|
|
30
34
|
{
|
|
31
35
|
size: "small",
|
|
32
36
|
variant: "text",
|
|
@@ -34,7 +38,7 @@ function LanguageToggle({ inverted = false, onChange, texts }) {
|
|
|
34
38
|
"aria-haspopup": "menu",
|
|
35
39
|
"aria-expanded": Boolean(menuAnchor),
|
|
36
40
|
"aria-label": t("language.choose", { name: current.label }),
|
|
37
|
-
|
|
41
|
+
ownSx: buttonSx,
|
|
38
42
|
children: current.code.toUpperCase()
|
|
39
43
|
}
|
|
40
44
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LanguageToggle.js","sources":["../../../src/ui/navigation/LanguageToggle.jsx"],"sourcesContent":["// El botón de idioma de la barra y de las pantallas de entrada.\n//\n// Los idiomas son los que ofrece la app (ToolkitProvider `languages`; por omisión español e inglés):\n// - con dos, un botón que cambia al otro (dice su código: «EN»);\n// - con tres o más, el botón dice el de ahora y abre un menú con cada uno en su propio idioma («Português»), como lo busca\n// quien lo habla; el elegido con su palomita.\n//\n// Solo avisa el idioma nuevo (onLanguageChange de ToolkitProvider). Lo demás lo hace el proyecto: su i18n, el header\n// Accept-Language de las peticiones y volver a pedir los datos del servidor, que traen textos traducidos. Las traducciones de\n// un idioma que agregue el proyecto van en `texts` de ToolkitProvider: el paquete solo trae español e inglés.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Button from '@mui/material/Button';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\n\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {DEFAULT_LANGUAGES} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\n\n/** Los idiomas que traen los textos del paquete. */\nexport const LANGUAGES = Object.freeze(DEFAULT_LANGUAGES.map((entry) => entry.code));\n\n/**\n * @param {object} [props]\n * @param {boolean} [props.inverted] texto blanco para fondos oscuros\n * @param {(language: string) => void} [props.onChange] además de avisar a ToolkitProvider, cada que se elige un idioma\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {language: {switchToNamed, choose}}\n */\nexport default function LanguageToggle({inverted = false, onChange, texts})
|
|
1
|
+
{"version":3,"file":"LanguageToggle.js","sources":["../../../src/ui/navigation/LanguageToggle.jsx"],"sourcesContent":["// El botón de idioma de la barra y de las pantallas de entrada.\n//\n// Los idiomas son los que ofrece la app (ToolkitProvider `languages`; por omisión español e inglés):\n// - con dos, un botón que cambia al otro (dice su código: «EN»);\n// - con tres o más, el botón dice el de ahora y abre un menú con cada uno en su propio idioma («Português»), como lo busca\n// quien lo habla; el elegido con su palomita.\n//\n// Solo avisa el idioma nuevo (onLanguageChange de ToolkitProvider). Lo demás lo hace el proyecto: su i18n, el header\n// Accept-Language de las peticiones y volver a pedir los datos del servidor, que traen textos traducidos. Las traducciones de\n// un idioma que agregue el proyecto van en `texts` de ToolkitProvider: el paquete solo trae español e inglés.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaLanguageToggle con defaultProps y\n// styleOverrides de sus partes: button.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useState} from 'react';\nimport Button from '@mui/material/Button';\nimport Menu from '@mui/material/Menu';\nimport MenuItem from '@mui/material/MenuItem';\n\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {DEFAULT_LANGUAGES} from '@/toolkit/toolkitSettings.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaLanguageToggle)\nconst THEME_NAME = 'CalcitaLanguageToggle';\nconst LanguageButton = piecePart(THEME_NAME, 'Button', Button);\n\n/** Los idiomas que traen los textos del paquete. */\nexport const LANGUAGES = Object.freeze(DEFAULT_LANGUAGES.map((entry) => entry.code));\n\n/**\n * @param {object} [props]\n * @param {boolean} [props.inverted] texto blanco para fondos oscuros\n * @param {(language: string) => void} [props.onChange] además de avisar a ToolkitProvider, cada que se elige un idioma\n * @param {object} [props.texts] los textos que cambian, con la forma de `common`: {language: {switchToNamed, choose}}\n */\nexport default function LanguageToggle(props) {\n // lo que el proyecto puso en su tema (components.CalcitaLanguageToggle.defaultProps) y encima lo que llega aquí\n const {inverted = false, onChange, texts} = usePieceProps(props, THEME_NAME);\n const {t, language, changeLanguage} = useToolkitTranslation('common', texts);\n const {languages = DEFAULT_LANGUAGES} = useToolkitSettings();\n const [menuAnchor, setMenuAnchor] = useState(null);\n const current = languages.find((entry) => entry.code === language) ?? languages[0];\n const buttonSx = {minWidth: 0, fontWeight: 700, letterSpacing: '0.08em', color: inverted ? 'common.white' : 'text.secondary'};\n\n const choose = (code) => {\n setMenuAnchor(null);\n changeLanguage(code);\n onChange?.(code);\n };\n\n // con dos: un botón que cambia al otro\n if (languages.length <= 2) {\n const next = languages.find((entry) => entry.code !== current.code) ?? current;\n // el aviso de siempre para español e inglés; para otro idioma, con su nombre\n const ariaLabel = t(`language.switchTo.${next.code}`, {defaultValue: t('language.switchToNamed', {name: next.label})});\n return (\n <LanguageButton size=\"small\" variant=\"text\" onClick={() => choose(next.code)} lang={next.code} aria-label={ariaLabel} ownSx={buttonSx}>\n {next.code.toUpperCase()}\n </LanguageButton>\n );\n }\n\n // con tres o más: el de ahora y un menú con todos\n return (\n <>\n <LanguageButton size=\"small\" variant=\"text\" onClick={(event) => setMenuAnchor(event.currentTarget)} aria-haspopup=\"menu\"\n aria-expanded={Boolean(menuAnchor)} aria-label={t('language.choose', {name: current.label})} ownSx={buttonSx}>\n {current.code.toUpperCase()}\n </LanguageButton>\n <Menu anchorEl={menuAnchor} open={Boolean(menuAnchor)} onClose={() => setMenuAnchor(null)}>\n {languages.map((entry) => (\n <MenuItem key={entry.code} lang={entry.code} selected={entry.code === current.code} onClick={() => choose(entry.code)}>\n {entry.label}\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AA0BA,MAAM,aAAa;AACnB,MAAM,iBAAiB,UAAU,YAAY,UAAU,MAAM;AAGtD,MAAM,YAAY,OAAO,OAAO,kBAAkB,IAAI,CAAC,UAAU,MAAM,IAAI,CAAC;AAQnF,SAAwB,eAAe,OAAO;AAE1C,QAAM,EAAC,WAAW,OAAO,UAAU,UAAS,cAAc,OAAO,UAAU;AAC3E,QAAM,EAAC,GAAG,UAAU,mBAAkB,sBAAsB,UAAU,KAAK;AAC3E,QAAM,EAAC,YAAY,kBAAA,IAAqB,mBAAA;AACxC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,IAAI;AACjD,QAAM,UAAU,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ,KAAK,UAAU,CAAC;AACjF,QAAM,WAAW,EAAC,UAAU,GAAG,YAAY,KAAK,eAAe,UAAU,OAAO,WAAW,iBAAiB,iBAAA;AAE5G,QAAM,SAAS,CAAC,SAAS;AACrB,kBAAc,IAAI;AAClB,mBAAe,IAAI;AACnB,eAAW,IAAI;AAAA,EACnB;AAGA,MAAI,UAAU,UAAU,GAAG;AACvB,UAAM,OAAO,UAAU,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ,IAAI,KAAK;AAEvE,UAAM,YAAY,EAAE,qBAAqB,KAAK,IAAI,IAAI,EAAC,cAAc,EAAE,0BAA0B,EAAC,MAAM,KAAK,MAAA,CAAM,GAAE;AACrH,WACI,oBAAC,kBAAe,MAAK,SAAQ,SAAQ,QAAO,SAAS,MAAM,OAAO,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,cAAY,WAAW,OAAO,UACxH,UAAA,KAAK,KAAK,YAAA,EAAY,CAC3B;AAAA,EAER;AAGA,SACI,qBAAA,UAAA,EACI,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QAAe,MAAK;AAAA,QAAQ,SAAQ;AAAA,QAAO,SAAS,CAAC,UAAU,cAAc,MAAM,aAAa;AAAA,QAAG,iBAAc;AAAA,QAC1G,iBAAe,QAAQ,UAAU;AAAA,QAAG,cAAY,EAAE,mBAAmB,EAAC,MAAM,QAAQ,OAAM;AAAA,QAAG,OAAO;AAAA,QACvG,UAAA,QAAQ,KAAK,YAAA;AAAA,MAAY;AAAA,IAAA;AAAA,wBAE7B,MAAA,EAAK,UAAU,YAAY,MAAM,QAAQ,UAAU,GAAG,SAAS,MAAM,cAAc,IAAI,GACnF,oBAAU,IAAI,CAAC,UACZ,oBAAC,UAAA,EAA0B,MAAM,MAAM,MAAM,UAAU,MAAM,SAAS,QAAQ,MAAM,SAAS,MAAM,OAAO,MAAM,IAAI,GAC/G,UAAA,MAAM,SADI,MAAM,IAErB,CACH,EAAA,CACL;AAAA,EAAA,GACJ;AAER;"}
|
|
@@ -8,18 +8,24 @@ import Typography from "@mui/material/Typography";
|
|
|
8
8
|
import { textSizeOf } from "../../theme/appleTracking.js";
|
|
9
9
|
import ScrollableRow from "../layout/ScrollableRow.js";
|
|
10
10
|
import NavigateNextIcon from "@mui/icons-material/NavigateNext";
|
|
11
|
-
|
|
11
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
12
|
+
const THEME_NAME = "CalcitaPageBreadcrumbs";
|
|
13
|
+
const BreadcrumbsRoot = piecePart(THEME_NAME, "Root", Breadcrumbs);
|
|
14
|
+
const BreadcrumbsLink = piecePart(THEME_NAME, "Link", Link);
|
|
15
|
+
const BreadcrumbsCurrent = piecePart(THEME_NAME, "Current", Typography);
|
|
16
|
+
function PageBreadcrumbs(props) {
|
|
17
|
+
const { items, texts } = usePieceProps(props, THEME_NAME);
|
|
12
18
|
const { t } = useToolkitTranslation("layout", texts);
|
|
13
19
|
const { Link: RouterLink } = useToolkitNavigation();
|
|
14
20
|
if (items.length === 0) {
|
|
15
21
|
return null;
|
|
16
22
|
}
|
|
17
23
|
return /* @__PURE__ */ jsx(ScrollableRow, { py: 0, children: /* @__PURE__ */ jsx(
|
|
18
|
-
|
|
24
|
+
BreadcrumbsRoot,
|
|
19
25
|
{
|
|
20
26
|
"aria-label": t("breadcrumbs"),
|
|
21
27
|
separator: /* @__PURE__ */ jsx(NavigateNextIcon, { fontSize: "small" }),
|
|
22
|
-
|
|
28
|
+
ownSx: {
|
|
23
29
|
...textSizeOf(13),
|
|
24
30
|
color: "text.secondary",
|
|
25
31
|
flex: "0 0 auto",
|
|
@@ -29,14 +35,14 @@ function PageBreadcrumbs({ items, texts }) {
|
|
|
29
35
|
children: items.map((item, index) => {
|
|
30
36
|
const isLast = index === items.length - 1;
|
|
31
37
|
if (!isLast && item.to) {
|
|
32
|
-
return /* @__PURE__ */ jsx(
|
|
38
|
+
return /* @__PURE__ */ jsx(BreadcrumbsLink, { component: RouterLink, to: item.to, underline: "hover", color: "inherit", ownSx: { fontSize: "inherit" }, children: item.label }, `${item.to}-${index}`);
|
|
33
39
|
}
|
|
34
40
|
return /* @__PURE__ */ jsx(
|
|
35
|
-
|
|
41
|
+
BreadcrumbsCurrent,
|
|
36
42
|
{
|
|
37
43
|
component: "span",
|
|
38
44
|
"aria-current": isLast ? "page" : void 0,
|
|
39
|
-
|
|
45
|
+
ownSx: { fontSize: "inherit", fontWeight: isLast ? 500 : 400, color: isLast ? "text.primary" : "text.secondary" },
|
|
40
46
|
children: item.label
|
|
41
47
|
},
|
|
42
48
|
`${item.label}-${index}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageBreadcrumbs.js","sources":["../../../src/ui/navigation/PageBreadcrumbs.jsx"],"sourcesContent":["// Las migas de una pantalla (Inicio › Catálogos › Productos): cada una lleva a su pantalla menos la última, que es donde\n// ya está la persona. En el teléfono no se parten en dos renglones: se deslizan de lado en la misma tira que los filtros\n// rápidos (ScrollableRow: sin barra de desplazamiento, la orilla donde hay más se desvanece y se arrastra con el mouse).\n// Antes deslizaba la lista de MUI y salía la barra gris del navegador debajo de las migas.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport Breadcrumbs from '@mui/material/Breadcrumbs';\nimport Link from '@mui/material/Link';\nimport Typography from '@mui/material/Typography';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport NavigateNextIcon from '@mui/icons-material/NavigateNext';\n\n/**\n * @param {object} props\n * @param {Array<{label: string, to?: string}>} props.items textos ya traducidos\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nexport default function PageBreadcrumbs({items, texts})
|
|
1
|
+
{"version":3,"file":"PageBreadcrumbs.js","sources":["../../../src/ui/navigation/PageBreadcrumbs.jsx"],"sourcesContent":["// Las migas de una pantalla (Inicio › Catálogos › Productos): cada una lleva a su pantalla menos la última, que es donde\n// ya está la persona. En el teléfono no se parten en dos renglones: se deslizan de lado en la misma tira que los filtros\n// rápidos (ScrollableRow: sin barra de desplazamiento, la orilla donde hay más se desvanece y se arrastra con el mouse).\n// Antes deslizaba la lista de MUI y salía la barra gris del navegador debajo de las migas.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaPageBreadcrumbs con defaultProps y\n// styleOverrides de sus partes: root, link, current.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {useToolkitNavigation} from '@/toolkit/toolkitContext.js';\nimport Breadcrumbs from '@mui/material/Breadcrumbs';\nimport Link from '@mui/material/Link';\nimport Typography from '@mui/material/Typography';\nimport {textSizeOf} from '@/theme/appleTracking.js';\nimport ScrollableRow from '@/ui/layout/ScrollableRow.jsx';\nimport NavigateNextIcon from '@mui/icons-material/NavigateNext';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaPageBreadcrumbs)\nconst THEME_NAME = 'CalcitaPageBreadcrumbs';\nconst BreadcrumbsRoot = piecePart(THEME_NAME, 'Root', Breadcrumbs);\nconst BreadcrumbsLink = piecePart(THEME_NAME, 'Link', Link);\nconst BreadcrumbsCurrent = piecePart(THEME_NAME, 'Current', Typography);\n\n/**\n * @param {object} props\n * @param {Array<{label: string, to?: string}>} props.items textos ya traducidos\n * @param {object} [props.texts] los textos que cambian, con la forma de `layout` en los textos del paquete\n */\nexport default function PageBreadcrumbs(props) {\n // lo que el proyecto puso en su tema (components.CalcitaPageBreadcrumbs.defaultProps) y encima lo que llega aquí\n const {items, texts} = usePieceProps(props, THEME_NAME);\n const {t} = useToolkitTranslation('layout', texts);\n const {Link: RouterLink} = useToolkitNavigation();\n if (items.length === 0) {\n return null;\n }\n return (\n <ScrollableRow py={0}>\n <BreadcrumbsRoot aria-label={t('breadcrumbs')} separator={<NavigateNextIcon fontSize=\"small\" />}\n ownSx={{...textSizeOf(13), color: 'text.secondary', flex: '0 0 auto', '& .MuiBreadcrumbs-ol': {flexWrap: 'nowrap'},\n '& .MuiBreadcrumbs-li': {flex: '0 0 auto'}}}>\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n if (!isLast && item.to) {\n return (\n <BreadcrumbsLink key={`${item.to}-${index}`} component={RouterLink} to={item.to} underline=\"hover\" color=\"inherit\" ownSx={{fontSize: 'inherit'}}>\n {item.label}\n </BreadcrumbsLink>\n );\n }\n return (\n <BreadcrumbsCurrent key={`${item.label}-${index}`} component=\"span\" aria-current={isLast ? 'page' : undefined}\n ownSx={{fontSize: 'inherit', fontWeight: isLast ? 500 : 400, color: isLast ? 'text.primary' : 'text.secondary'}}>\n {item.label}\n </BreadcrumbsCurrent>\n );\n })}\n </BreadcrumbsRoot>\n </ScrollableRow>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAoBA,MAAM,aAAa;AACnB,MAAM,kBAAkB,UAAU,YAAY,QAAQ,WAAW;AACjE,MAAM,kBAAkB,UAAU,YAAY,QAAQ,IAAI;AAC1D,MAAM,qBAAqB,UAAU,YAAY,WAAW,UAAU;AAOtE,SAAwB,gBAAgB,OAAO;AAE3C,QAAM,EAAC,OAAO,MAAA,IAAS,cAAc,OAAO,UAAU;AACtD,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,EAAC,MAAM,WAAA,IAAc,qBAAA;AAC3B,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AACA,SACI,oBAAC,eAAA,EAAc,IAAI,GACf,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MAAgB,cAAY,EAAE,aAAa;AAAA,MAAG,WAAW,oBAAC,kBAAA,EAAiB,UAAS,QAAA,CAAQ;AAAA,MAChF,OAAO;AAAA,QAAC,GAAG,WAAW,EAAE;AAAA,QAAG,OAAO;AAAA,QAAkB,MAAM;AAAA,QAAY,wBAAwB,EAAC,UAAU,SAAA;AAAA,QACpG,wBAAwB,EAAC,MAAM,WAAA;AAAA,MAAU;AAAA,MACtD,UAAA,MAAM,IAAI,CAAC,MAAM,UAAU;AACxB,cAAM,SAAS,UAAU,MAAM,SAAS;AACxC,YAAI,CAAC,UAAU,KAAK,IAAI;AACpB,iBACI,oBAAC,mBAA4C,WAAW,YAAY,IAAI,KAAK,IAAI,WAAU,SAAQ,OAAM,WAAU,OAAO,EAAC,UAAU,UAAA,GAChI,UAAA,KAAK,MAAA,GADY,GAAG,KAAK,EAAE,IAAI,KAAK,EAEzC;AAAA,QAER;AACA,eACI;AAAA,UAAC;AAAA,UAAA;AAAA,YAAkD,WAAU;AAAA,YAAO,gBAAc,SAAS,SAAS;AAAA,YACxF,OAAO,EAAC,UAAU,WAAW,YAAY,SAAS,MAAM,KAAK,OAAO,SAAS,iBAAiB,iBAAA;AAAA,YACrG,UAAA,KAAK;AAAA,UAAA;AAAA,UAFe,GAAG,KAAK,KAAK,IAAI,KAAK;AAAA,QAAA;AAAA,MAKvD,CAAC;AAAA,IAAA;AAAA,EAAA,GAET;AAER;"}
|
|
@@ -19,10 +19,23 @@ import { useTheme } from "@mui/material/styles";
|
|
|
19
19
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
20
20
|
import { paletteOf } from "../../theme/components/paletteOf.js";
|
|
21
21
|
import { TIMELINE_EMPHASIS, timelineDotOf } from "./timelineDot.js";
|
|
22
|
+
import { piecePart, usePieceProps } from "../../theme/themePieces.js";
|
|
23
|
+
const THEME_NAME = "CalcitaGroupedTimeline";
|
|
24
|
+
const TimelineGroup = piecePart(THEME_NAME, "Group", Box);
|
|
25
|
+
const TimelineGroupHeader = piecePart(THEME_NAME, "GroupHeader", Stack);
|
|
26
|
+
const TimelineGroupButton = piecePart(THEME_NAME, "GroupButton", ButtonBase);
|
|
27
|
+
const TimelineGroupTitle = piecePart(THEME_NAME, "GroupTitle", Typography);
|
|
28
|
+
const TimelineExpandIcon = piecePart(THEME_NAME, "ExpandIcon", ExpandMoreIcon);
|
|
29
|
+
const TimelineTimeline = piecePart(THEME_NAME, "Timeline", Timeline);
|
|
30
|
+
const TimelineOpposite = piecePart(THEME_NAME, "Opposite", TimelineOppositeContent);
|
|
31
|
+
const TimelineConnectorPart = piecePart(THEME_NAME, "Connector", TimelineConnector);
|
|
32
|
+
const TimelineContentPart = piecePart(THEME_NAME, "Content", TimelineContent);
|
|
33
|
+
const TimelineEntryTitle = piecePart(THEME_NAME, "EntryTitle", Typography);
|
|
22
34
|
const HIGHLIGHT_TINT_PERCENT = 8;
|
|
23
35
|
const LEFT_ALIGNED_TIMELINE = { m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: { flex: 0, p: 0 } };
|
|
24
36
|
const ALTERNATE_TIMELINE = { m: 0, px: 0, py: 0.5 };
|
|
25
|
-
function GroupedTimeline(
|
|
37
|
+
function GroupedTimeline(props) {
|
|
38
|
+
const { groups, onGroupToggle } = usePieceProps(props, THEME_NAME);
|
|
26
39
|
const theme = useTheme();
|
|
27
40
|
const isWide = useMediaQuery(theme.breakpoints.up("md"));
|
|
28
41
|
const [expandedKeys, setExpandedKeys] = useState(() => new Set(
|
|
@@ -47,7 +60,7 @@ function GroupedTimeline({ groups, onGroupToggle }) {
|
|
|
47
60
|
const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);
|
|
48
61
|
return (
|
|
49
62
|
// el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado
|
|
50
|
-
/* @__PURE__ */ jsxs(
|
|
63
|
+
/* @__PURE__ */ jsxs(TimelineGroup, { ownSx: {
|
|
51
64
|
border: 1,
|
|
52
65
|
borderRadius: 1.5,
|
|
53
66
|
px: 1.5,
|
|
@@ -57,18 +70,18 @@ function GroupedTimeline({ groups, onGroupToggle }) {
|
|
|
57
70
|
// un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo
|
|
58
71
|
bgcolor: (theme2) => isHighlighted ? `color-mix(in srgb, ${paletteOf(theme2).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : "transparent"
|
|
59
72
|
}, children: [
|
|
60
|
-
timelineGroup.title && /* @__PURE__ */ jsxs(
|
|
73
|
+
timelineGroup.title && /* @__PURE__ */ jsxs(TimelineGroupHeader, { direction: "row", spacing: 1, ownSx: { alignItems: "center", justifyContent: isWide ? "center" : "flex-start" }, children: [
|
|
61
74
|
/* @__PURE__ */ jsxs(
|
|
62
|
-
|
|
75
|
+
TimelineGroupButton,
|
|
63
76
|
{
|
|
64
77
|
disabled: !isCollapsible,
|
|
65
78
|
onClick: () => toggleGroup(timelineGroup.key),
|
|
66
79
|
"aria-expanded": isCollapsible ? isExpanded : void 0,
|
|
67
|
-
|
|
80
|
+
ownSx: { gap: 1, px: 1, py: 0.25, borderRadius: 1, "&.Mui-disabled": { pointerEvents: "none" } },
|
|
68
81
|
children: [
|
|
69
|
-
/* @__PURE__ */ jsx(
|
|
82
|
+
/* @__PURE__ */ jsx(TimelineGroupTitle, { variant: "h6", component: "h3", ownSx: { fontWeight: 700, color: isMuted ? "text.secondary" : "text.primary" }, children: timelineGroup.title }),
|
|
70
83
|
isCollapsible && timelineGroup.summary && /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", component: "span", children: timelineGroup.summary }),
|
|
71
|
-
isCollapsible && /* @__PURE__ */ jsx(
|
|
84
|
+
isCollapsible && /* @__PURE__ */ jsx(TimelineExpandIcon, { fontSize: "small", ownSx: {
|
|
72
85
|
color: "text.secondary",
|
|
73
86
|
transition: theme.transitions.create("transform"),
|
|
74
87
|
transform: isExpanded ? "rotate(180deg)" : "none"
|
|
@@ -78,19 +91,19 @@ function GroupedTimeline({ groups, onGroupToggle }) {
|
|
|
78
91
|
),
|
|
79
92
|
timelineGroup.badge && /* @__PURE__ */ jsx(Chip, { size: "small", color: "primary", label: timelineGroup.badge })
|
|
80
93
|
] }),
|
|
81
|
-
/* @__PURE__ */ jsx(Collapse, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ jsx(
|
|
94
|
+
/* @__PURE__ */ jsx(Collapse, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ jsx(TimelineTimeline, { position: isWide ? "alternate" : "right", ownSx: isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE, children: timelineGroup.items.map((timelineEntry, entryPosition) => {
|
|
82
95
|
const EntryIcon = timelineEntry.icon;
|
|
83
96
|
const isFirstEntry = entryPosition === 0;
|
|
84
97
|
const isLastEntry = entryPosition === timelineGroup.items.length - 1;
|
|
85
98
|
return /* @__PURE__ */ jsxs(TimelineItem, { children: [
|
|
86
|
-
isWide && /* @__PURE__ */ jsx(
|
|
99
|
+
isWide && /* @__PURE__ */ jsx(TimelineOpposite, { variant: "body2", color: "text.secondary", ownSx: { m: "auto 0" }, children: timelineEntry.timestamp }),
|
|
87
100
|
/* @__PURE__ */ jsxs(TimelineSeparator, { children: [
|
|
88
|
-
/* @__PURE__ */ jsx(
|
|
101
|
+
/* @__PURE__ */ jsx(TimelineConnectorPart, { ownSx: { visibility: isFirstEntry ? "hidden" : "visible" } }),
|
|
89
102
|
/* @__PURE__ */ jsx(TimelineDot, { ...timelineDotOf({ emphasis, tone: timelineEntry.tone }), children: EntryIcon && /* @__PURE__ */ jsx(EntryIcon, { fontSize: "small" }) }),
|
|
90
|
-
/* @__PURE__ */ jsx(
|
|
103
|
+
/* @__PURE__ */ jsx(TimelineConnectorPart, { ownSx: { visibility: isLastEntry ? "hidden" : "visible" } })
|
|
91
104
|
] }),
|
|
92
|
-
/* @__PURE__ */ jsxs(
|
|
93
|
-
/* @__PURE__ */ jsx(
|
|
105
|
+
/* @__PURE__ */ jsxs(TimelineContentPart, { ownSx: { py: 1.5, px: 2, minWidth: 0 }, children: [
|
|
106
|
+
/* @__PURE__ */ jsx(TimelineEntryTitle, { variant: "body2", component: "p", ownSx: { fontWeight: 600, color: isMuted ? "text.secondary" : "text.primary" }, children: timelineEntry.title }),
|
|
94
107
|
!isWide && timelineEntry.timestamp && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "text.secondary", component: "p", children: timelineEntry.timestamp }),
|
|
95
108
|
timelineEntry.details
|
|
96
109
|
] })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupedTimeline.js","sources":["../../../src/ui/timeline/GroupedTimeline.jsx"],"sourcesContent":["// Línea de tiempo por grupos (Timeline de @mui/lab al estilo del ejemplo «Customization» de MUI): cada grupo con su\n// título y una insignia opcional, y cada evento con su ícono dentro del punto, su título, su hora y un detalle libre.\n//\n// Uso típico: el historial de un registro agrupado por etapa o por intento (detalle de un pedido: un grupo por intento de entrega,\n// el vigente resaltado). El énfasis de cada grupo (TIMELINE_EMPHASIS) decide cómo se pintan sus puntos (timelineDot.js).\n//\n// En pantalla ancha los eventos alternan de lado y la hora va del otro lado del punto; en celular todo va de un lado, con\n// la hora abajo del título, para que quepa.\n//\n// Un grupo plegable (isCollapsible) enseña solo su título y su resumen hasta que se abre: sirve para que lo viejo no\n// obligue a bajar (los intentos anteriores van plegados).\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: títulos, horas y detalles llegan ya traducidos y\n// formateados, y el ícono y el tono de cada evento los decide quien la usa (con sus diccionarios).\nimport {useState} from 'react';\nimport Timeline from '@mui/lab/Timeline';\nimport TimelineConnector from '@mui/lab/TimelineConnector';\nimport TimelineContent from '@mui/lab/TimelineContent';\nimport TimelineDot from '@mui/lab/TimelineDot';\nimport TimelineItem, {timelineItemClasses} from '@mui/lab/TimelineItem';\nimport TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';\nimport TimelineSeparator from '@mui/lab/TimelineSeparator';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Collapse from '@mui/material/Collapse';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport {useTheme} from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {TIMELINE_EMPHASIS, timelineDotOf} from '@/ui/timeline/timelineDot.js';\n\n// el fondo del grupo resaltado: cuánto del color principal (por ciento)\nconst HIGHLIGHT_TINT_PERCENT = 8;\n// en celular la línea va pegada a la izquierda: sin la columna de enfrente que @mui/lab reserva por omisión\nconst LEFT_ALIGNED_TIMELINE = {m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: {flex: 0, p: 0}};\nconst ALTERNATE_TIMELINE = {m: 0, px: 0, py: 0.5};\n\n/**\n * @param {object} props\n * @param {Object[]} props.groups\n * @param {string} props.groups[].key\n * @param {string} [props.groups[].title] texto ya traducido; sin título el grupo no pinta encabezado\n * @param {string} [props.groups[].badge] insignia junto al título (p. ej. «Intento actual»)\n * @param {string} [props.groups[].summary] lo que se lee junto al título del grupo plegado (p. ej. «6 eventos»)\n * @param {boolean} [props.groups[].isCollapsible] el título se vuelve un botón que abre y cierra el grupo (necesita\n * title)\n * @param {boolean} [props.groups[].defaultExpanded] si un grupo plegable arranca abierto (por omisión, sí)\n * @param {'highlighted'|'normal'|'muted'} [props.groups[].emphasis]\n * @param {Object[]} props.groups[].items\n * @param {string} props.groups[].items[].key\n * @param {import('react').ElementType} [props.groups[].items[].icon] ícono dentro del punto\n * @param {string} [props.groups[].items[].tone] success, warning, error, info, primary o grey\n * @param {import('react').ReactNode} props.groups[].items[].title\n * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas\n * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)\n * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,\n * para pedir sus eventos hasta que se abre)\n */\nexport default function GroupedTimeline({groups, onGroupToggle}) {\n const theme = useTheme();\n const isWide = useMediaQuery(theme.breakpoints.up('md'));\n // llaves de los grupos plegables que el usuario tiene abiertos; arrancan como dice defaultExpanded\n const [expandedKeys, setExpandedKeys] = useState(() => new Set(\n groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key),\n ));\n const toggleGroup = (groupKey) => {\n const isOpening = !expandedKeys.has(groupKey);\n const nextKeys = new Set(expandedKeys);\n if (isOpening) {\n nextKeys.add(groupKey);\n } else {\n nextKeys.delete(groupKey);\n }\n setExpandedKeys(nextKeys);\n onGroupToggle?.(groupKey, isOpening);\n };\n\n return (\n <Stack spacing={1.5}>\n {groups.map((timelineGroup) => {\n const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;\n const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;\n const isMuted = emphasis === TIMELINE_EMPHASIS.muted;\n const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);\n const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);\n\n return (\n // el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado\n <Box key={timelineGroup.key} sx={{\n border: 1, borderRadius: 1.5, px: 1.5, pt: 1.25, pb: isExpanded ? 0 : 1.25,\n borderColor: isHighlighted ? 'primary.main' : 'transparent',\n // un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo\n bgcolor: (theme) => (isHighlighted ? `color-mix(in srgb, ${paletteOf(theme).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : 'transparent'),\n }}>\n {timelineGroup.title && (\n <Stack direction=\"row\" spacing={1} sx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <ButtonBase disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n sx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <Typography variant=\"h6\" component=\"h3\" sx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </Typography>\n {/* plegado, el grupo dice cuántos eventos trae: se sabe qué hay sin abrirlo */}\n {isCollapsible && timelineGroup.summary && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\">{timelineGroup.summary}</Typography>\n )}\n {isCollapsible && (\n <ExpandMoreIcon fontSize=\"small\" sx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </ButtonBase>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </Stack>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <Timeline position={isWide ? 'alternate' : 'right'} sx={isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE}>\n {timelineGroup.items.map((timelineEntry, entryPosition) => {\n const EntryIcon = timelineEntry.icon;\n const isFirstEntry = entryPosition === 0;\n const isLastEntry = entryPosition === timelineGroup.items.length - 1;\n\n return (\n <TimelineItem key={timelineEntry.key}>\n {isWide && (\n <TimelineOppositeContent variant=\"body2\" color=\"text.secondary\" sx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOppositeContent>\n )}\n <TimelineSeparator>\n <TimelineConnector sx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnector sx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContent sx={{py: 1.5, px: 2, minWidth: 0}}>\n <Typography variant=\"body2\" component=\"p\" sx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </Typography>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContent>\n </TimelineItem>\n );\n })}\n </Timeline>\n </Collapse>\n </Box>\n );\n })}\n </Stack>\n );\n}\n"],"names":["theme"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GroupedTimeline.js","sources":["../../../src/ui/timeline/GroupedTimeline.jsx"],"sourcesContent":["// Línea de tiempo por grupos (Timeline de @mui/lab al estilo del ejemplo «Customization» de MUI): cada grupo con su\n// título y una insignia opcional, y cada evento con su ícono dentro del punto, su título, su hora y un detalle libre.\n//\n// Uso típico: el historial de un registro agrupado por etapa o por intento (detalle de un pedido: un grupo por intento de entrega,\n// el vigente resaltado). El énfasis de cada grupo (TIMELINE_EMPHASIS) decide cómo se pintan sus puntos (timelineDot.js).\n//\n// En pantalla ancha los eventos alternan de lado y la hora va del otro lado del punto; en celular todo va de un lado, con\n// la hora abajo del título, para que quepa.\n//\n// Un grupo plegable (isCollapsible) enseña solo su título y su resumen hasta que se abre: sirve para que lo viejo no\n// obligue a bajar (los intentos anteriores van plegados).\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaGroupedTimeline con defaultProps y\n// styleOverrides de sus partes: group, groupHeader, groupButton, groupTitle, expandIcon, timeline, opposite, connector,\n// content, entryTitle.\n//\n// Genérico del paquete: function (regla de la casa). No sabe de negocio: títulos, horas y detalles llegan ya traducidos y\n// formateados, y el ícono y el tono de cada evento los decide quien la usa (con sus diccionarios).\nimport {useState} from 'react';\nimport Timeline from '@mui/lab/Timeline';\nimport TimelineConnector from '@mui/lab/TimelineConnector';\nimport TimelineContent from '@mui/lab/TimelineContent';\nimport TimelineDot from '@mui/lab/TimelineDot';\nimport TimelineItem, {timelineItemClasses} from '@mui/lab/TimelineItem';\nimport TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';\nimport TimelineSeparator from '@mui/lab/TimelineSeparator';\nimport Box from '@mui/material/Box';\nimport ButtonBase from '@mui/material/ButtonBase';\nimport Chip from '@mui/material/Chip';\nimport Collapse from '@mui/material/Collapse';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport useMediaQuery from '@mui/material/useMediaQuery';\nimport {useTheme} from '@mui/material/styles';\nimport ExpandMoreIcon from '@mui/icons-material/ExpandMore';\n\nimport {paletteOf} from '@/theme/components/paletteOf.js';\nimport {TIMELINE_EMPHASIS, timelineDotOf} from '@/ui/timeline/timelineDot.js';\nimport {piecePart, usePieceProps} from '@/theme/themePieces.js';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaGroupedTimeline)\nconst THEME_NAME = 'CalcitaGroupedTimeline';\nconst TimelineGroup = piecePart(THEME_NAME, 'Group', Box);\nconst TimelineGroupHeader = piecePart(THEME_NAME, 'GroupHeader', Stack);\nconst TimelineGroupButton = piecePart(THEME_NAME, 'GroupButton', ButtonBase);\nconst TimelineGroupTitle = piecePart(THEME_NAME, 'GroupTitle', Typography);\nconst TimelineExpandIcon = piecePart(THEME_NAME, 'ExpandIcon', ExpandMoreIcon);\nconst TimelineTimeline = piecePart(THEME_NAME, 'Timeline', Timeline);\nconst TimelineOpposite = piecePart(THEME_NAME, 'Opposite', TimelineOppositeContent);\nconst TimelineConnectorPart = piecePart(THEME_NAME, 'Connector', TimelineConnector);\nconst TimelineContentPart = piecePart(THEME_NAME, 'Content', TimelineContent);\nconst TimelineEntryTitle = piecePart(THEME_NAME, 'EntryTitle', Typography);\n\n// el fondo del grupo resaltado: cuánto del color principal (por ciento)\nconst HIGHLIGHT_TINT_PERCENT = 8;\n// en celular la línea va pegada a la izquierda: sin la columna de enfrente que @mui/lab reserva por omisión\nconst LEFT_ALIGNED_TIMELINE = {m: 0, px: 0, py: 0.5, [`& .${timelineItemClasses.root}::before`]: {flex: 0, p: 0}};\nconst ALTERNATE_TIMELINE = {m: 0, px: 0, py: 0.5};\n\n/**\n * @param {object} props\n * @param {Object[]} props.groups\n * @param {string} props.groups[].key\n * @param {string} [props.groups[].title] texto ya traducido; sin título el grupo no pinta encabezado\n * @param {string} [props.groups[].badge] insignia junto al título (p. ej. «Intento actual»)\n * @param {string} [props.groups[].summary] lo que se lee junto al título del grupo plegado (p. ej. «6 eventos»)\n * @param {boolean} [props.groups[].isCollapsible] el título se vuelve un botón que abre y cierra el grupo (necesita\n * title)\n * @param {boolean} [props.groups[].defaultExpanded] si un grupo plegable arranca abierto (por omisión, sí)\n * @param {'highlighted'|'normal'|'muted'} [props.groups[].emphasis]\n * @param {Object[]} props.groups[].items\n * @param {string} props.groups[].items[].key\n * @param {import('react').ElementType} [props.groups[].items[].icon] ícono dentro del punto\n * @param {string} [props.groups[].items[].tone] success, warning, error, info, primary o grey\n * @param {import('react').ReactNode} props.groups[].items[].title\n * @param {string} [props.groups[].items[].timestamp] fecha y hora ya formateadas\n * @param {import('react').ReactNode} [props.groups[].items[].details] lo que va abajo del título (chip de resultado, una liga...)\n * @param {(groupKey: string, isExpanded: boolean) => void} [props.onGroupToggle] la persona abrió o plegó un grupo (por ejemplo,\n * para pedir sus eventos hasta que se abre)\n */\nexport default function GroupedTimeline(props) {\n // lo que el proyecto puso en su tema (components.CalcitaGroupedTimeline.defaultProps) y encima lo que llega aquí\n const {groups, onGroupToggle} = usePieceProps(props, THEME_NAME);\n const theme = useTheme();\n const isWide = useMediaQuery(theme.breakpoints.up('md'));\n // llaves de los grupos plegables que el usuario tiene abiertos; arrancan como dice defaultExpanded\n const [expandedKeys, setExpandedKeys] = useState(() => new Set(\n groups.filter((timelineGroup) => timelineGroup.defaultExpanded !== false).map((timelineGroup) => timelineGroup.key),\n ));\n const toggleGroup = (groupKey) => {\n const isOpening = !expandedKeys.has(groupKey);\n const nextKeys = new Set(expandedKeys);\n if (isOpening) {\n nextKeys.add(groupKey);\n } else {\n nextKeys.delete(groupKey);\n }\n setExpandedKeys(nextKeys);\n onGroupToggle?.(groupKey, isOpening);\n };\n\n return (\n <Stack spacing={1.5}>\n {groups.map((timelineGroup) => {\n const emphasis = timelineGroup.emphasis ?? TIMELINE_EMPHASIS.normal;\n const isHighlighted = emphasis === TIMELINE_EMPHASIS.highlighted;\n const isMuted = emphasis === TIMELINE_EMPHASIS.muted;\n const isCollapsible = Boolean(timelineGroup.isCollapsible && timelineGroup.title);\n const isExpanded = !isCollapsible || expandedKeys.has(timelineGroup.key);\n\n return (\n // el borde transparente en los demás grupos mantiene todo alineado con el grupo resaltado\n <TimelineGroup key={timelineGroup.key} ownSx={{\n border: 1, borderRadius: 1.5, px: 1.5, pt: 1.25, pb: isExpanded ? 0 : 1.25,\n borderColor: isHighlighted ? 'primary.main' : 'transparent',\n // un tinte del color principal: primary.lighter no existe en el tema de MUI y el grupo se quedaba sin fondo\n bgcolor: (theme) => (isHighlighted ? `color-mix(in srgb, ${paletteOf(theme).primary.main} ${HIGHLIGHT_TINT_PERCENT}%, transparent)` : 'transparent'),\n }}>\n {timelineGroup.title && (\n <TimelineGroupHeader direction=\"row\" spacing={1} ownSx={{alignItems: 'center', justifyContent: isWide ? 'center' : 'flex-start'}}>\n <TimelineGroupButton disabled={!isCollapsible} onClick={() => toggleGroup(timelineGroup.key)}\n aria-expanded={isCollapsible ? isExpanded : undefined}\n ownSx={{gap: 1, px: 1, py: 0.25, borderRadius: 1, '&.Mui-disabled': {pointerEvents: 'none'}}}>\n <TimelineGroupTitle variant=\"h6\" component=\"h3\" ownSx={{fontWeight: 700, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineGroup.title}\n </TimelineGroupTitle>\n {/* plegado, el grupo dice cuántos eventos trae: se sabe qué hay sin abrirlo */}\n {isCollapsible && timelineGroup.summary && (\n <Typography variant=\"body2\" color=\"text.secondary\" component=\"span\">{timelineGroup.summary}</Typography>\n )}\n {isCollapsible && (\n <TimelineExpandIcon fontSize=\"small\" ownSx={{\n color: 'text.secondary', transition: theme.transitions.create('transform'),\n transform: isExpanded ? 'rotate(180deg)' : 'none',\n }} />\n )}\n </TimelineGroupButton>\n {timelineGroup.badge && <Chip size=\"small\" color=\"primary\" label={timelineGroup.badge} />}\n </TimelineGroupHeader>\n )}\n <Collapse in={isExpanded} unmountOnExit>\n <TimelineTimeline position={isWide ? 'alternate' : 'right'} ownSx={isWide ? ALTERNATE_TIMELINE : LEFT_ALIGNED_TIMELINE}>\n {timelineGroup.items.map((timelineEntry, entryPosition) => {\n const EntryIcon = timelineEntry.icon;\n const isFirstEntry = entryPosition === 0;\n const isLastEntry = entryPosition === timelineGroup.items.length - 1;\n\n return (\n <TimelineItem key={timelineEntry.key}>\n {isWide && (\n <TimelineOpposite variant=\"body2\" color=\"text.secondary\" ownSx={{m: 'auto 0'}}>\n {timelineEntry.timestamp}\n </TimelineOpposite>\n )}\n <TimelineSeparator>\n <TimelineConnectorPart ownSx={{visibility: isFirstEntry ? 'hidden' : 'visible'}} />\n <TimelineDot {...timelineDotOf({emphasis, tone: timelineEntry.tone})}>\n {EntryIcon && <EntryIcon fontSize=\"small\" />}\n </TimelineDot>\n <TimelineConnectorPart ownSx={{visibility: isLastEntry ? 'hidden' : 'visible'}} />\n </TimelineSeparator>\n <TimelineContentPart ownSx={{py: 1.5, px: 2, minWidth: 0}}>\n <TimelineEntryTitle variant=\"body2\" component=\"p\" ownSx={{fontWeight: 600, color: isMuted ? 'text.secondary' : 'text.primary'}}>\n {timelineEntry.title}\n </TimelineEntryTitle>\n {!isWide && timelineEntry.timestamp && (\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"p\">{timelineEntry.timestamp}</Typography>\n )}\n {timelineEntry.details}\n </TimelineContentPart>\n </TimelineItem>\n );\n })}\n </TimelineTimeline>\n </Collapse>\n </TimelineGroup>\n );\n })}\n </Stack>\n );\n}\n"],"names":["theme"],"mappings":"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"}
|