breadcrumbDropdowns.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import {
  2. branchPath,
  3. yearPath,
  4. monthPath,
  5. dayPath,
  6. } from "@/lib/frontend/routes";
  7. import { formatMonthLabel } from "@/lib/frontend/explorer/formatters";
  8. import { sortNumericStringsDesc } from "@/lib/frontend/explorer/sorters";
  9. /**
  10. * normalizeNumericOptions
  11. *
  12. * Pure helper:
  13. * - Ensures array input
  14. * - Filters empty values
  15. * - Deduplicates
  16. * - Sorts descending (latest first) for numeric strings
  17. *
  18. * @param {string[]|null|undefined} options
  19. * @returns {string[]|null}
  20. */
  21. export function normalizeNumericOptions(options) {
  22. if (!Array.isArray(options) || options.length === 0) return null;
  23. const unique = Array.from(
  24. new Set(options.map((x) => String(x)).filter(Boolean))
  25. );
  26. if (unique.length === 0) return null;
  27. return sortNumericStringsDesc(unique);
  28. }
  29. /**
  30. * buildExplorerDropdownItems
  31. *
  32. * Builds dropdown menu items for year/month/day breadcrumb segments.
  33. * This function is intentionally pure and React-free to keep it testable.
  34. *
  35. * @param {{
  36. * branch: string,
  37. * year?: string|null,
  38. * month?: string|null,
  39. * day?: string|null,
  40. * yearOptions?: string[]|null,
  41. * monthOptions?: string[]|null,
  42. * dayOptions?: string[]|null
  43. * }} input
  44. * @returns {{
  45. * yearItems: Array<{ value: string, label: string, href: string }> | null,
  46. * monthItems: Array<{ value: string, label: string, href: string }> | null,
  47. * dayItems: Array<{ value: string, label: string, href: string }> | null
  48. * }}
  49. */
  50. export function buildExplorerDropdownItems({
  51. branch,
  52. year = null,
  53. month = null,
  54. day = null,
  55. yearOptions = null,
  56. monthOptions = null,
  57. dayOptions = null,
  58. }) {
  59. const years = normalizeNumericOptions(yearOptions);
  60. const months = normalizeNumericOptions(monthOptions);
  61. const days = normalizeNumericOptions(dayOptions);
  62. const yearItems =
  63. year && years
  64. ? years.map((y) => ({
  65. value: y,
  66. label: y,
  67. href: yearPath(branch, y),
  68. }))
  69. : null;
  70. const monthItems =
  71. year && month && months
  72. ? months.map((m) => ({
  73. value: m,
  74. label: formatMonthLabel(m),
  75. href: monthPath(branch, year, m),
  76. }))
  77. : null;
  78. const dayItems =
  79. year && month && day && days
  80. ? days.map((d) => ({
  81. value: d,
  82. label: d,
  83. href: dayPath(branch, year, month, d),
  84. }))
  85. : null;
  86. return { yearItems, monthItems, dayItems };
  87. }
  88. /**
  89. * buildBranchCrumbHref
  90. *
  91. * Tiny helper to keep the breadcrumb component simple.
  92. *
  93. * @param {string} branch
  94. * @returns {string}
  95. */
  96. export function buildBranchCrumbHref(branch) {
  97. return branchPath(branch);
  98. }