SearchForm.jsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. "use client";
  2. import React from "react";
  3. import { SEARCH_SCOPE } from "@/lib/frontend/search/urlState";
  4. import SearchQueryBox from "@/components/search/form/SearchQueryBox";
  5. import SearchScopeSelect from "@/components/search/form/SearchScopeSelect";
  6. import SearchLimitSelect from "@/components/search/form/SearchLimitSelect";
  7. import SearchSingleBranchCombobox from "@/components/search/form/SearchSingleBranchCombobox";
  8. import SearchMultiBranchPicker from "@/components/search/form/SearchMultiBranchPicker";
  9. export default function SearchForm({
  10. branch,
  11. qDraft,
  12. onQDraftChange,
  13. onSubmit,
  14. currentQuery,
  15. isSubmitting,
  16. isAdminDev,
  17. scope,
  18. onScopeChange,
  19. onSingleBranchChange,
  20. availableBranches,
  21. branchesStatus,
  22. selectedBranches,
  23. onToggleBranch,
  24. onClearAllBranches,
  25. limit,
  26. onLimitChange,
  27. }) {
  28. const canSearch = typeof qDraft === "string" && qDraft.trim().length > 0;
  29. // RHL-038 UI behavior:
  30. // - Single scope shows an extra combobox (branch picker).
  31. // - Multi/All does not.
  32. // - We animate the combobox container (max-width/max-height/opacity) so the
  33. // query block (input + search button) grows/shrinks smoothly.
  34. const showSingleBranchCombobox = Boolean(
  35. isAdminDev && scope === SEARCH_SCOPE.SINGLE
  36. );
  37. return (
  38. <div className="space-y-4">
  39. <form
  40. onSubmit={(e) => {
  41. e.preventDefault();
  42. if (!canSearch) return;
  43. onSubmit();
  44. }}
  45. className="space-y-4"
  46. >
  47. {/*
  48. Layout goal:
  49. - Desktop: everything on one line (scope + optional single combobox | query | limit)
  50. - Mobile: stack to avoid horizontal overflow
  51. */}
  52. <div className="flex flex-col gap-3 lg:flex-row items-start lg:flex-nowrap">
  53. {/* Left block: scope + (animated) single-branch combobox */}
  54. {isAdminDev ? (
  55. <div className="flex items-end shrink-0">
  56. <SearchScopeSelect
  57. branch={branch}
  58. scope={scope}
  59. onScopeChange={onScopeChange}
  60. isSubmitting={isSubmitting}
  61. />
  62. {/*
  63. Animated presence (no mount/unmount):
  64. - When hidden: max-w-0/max-h-0 + opacity-0 so it takes no space.
  65. - When shown: expands to a reasonable max size.
  66. This animation drives the smooth resize of the query input block.
  67. */}
  68. <div
  69. className={[
  70. "overflow-hidden transition-all duration-200 ease-in-out",
  71. showSingleBranchCombobox
  72. ? "ml-2 max-w-44 max-h-24 opacity-100"
  73. : "ml-0 max-w-0 max-h-0 opacity-0 pointer-events-none",
  74. ].join(" ")}
  75. aria-hidden={showSingleBranchCombobox ? "false" : "true"}
  76. >
  77. <SearchSingleBranchCombobox
  78. branch={branch}
  79. branchesStatus={branchesStatus}
  80. availableBranches={availableBranches}
  81. onSingleBranchChange={onSingleBranchChange}
  82. // Disable when hidden so it can't be focused/clicked.
  83. isSubmitting={isSubmitting || !showSingleBranchCombobox}
  84. />
  85. </div>
  86. </div>
  87. ) : null}
  88. {/* Middle block: query must take the most space */}
  89. <div className="flex-1 min-w-0">
  90. <SearchQueryBox
  91. qDraft={qDraft}
  92. onQDraftChange={onQDraftChange}
  93. onSubmit={onSubmit}
  94. currentQuery={currentQuery}
  95. isSubmitting={isSubmitting}
  96. canSearch={canSearch}
  97. />
  98. </div>
  99. {/* Right block: limit stays “content-sized” */}
  100. <div className="shrink-0">
  101. <SearchLimitSelect
  102. limit={limit}
  103. onLimitChange={onLimitChange}
  104. isSubmitting={isSubmitting}
  105. />
  106. </div>
  107. </div>
  108. </form>
  109. {/* Multi scope branch picker remains below */}
  110. {isAdminDev && scope === SEARCH_SCOPE.MULTI ? (
  111. <SearchMultiBranchPicker
  112. branchesStatus={branchesStatus}
  113. availableBranches={availableBranches}
  114. selectedBranches={selectedBranches}
  115. onToggleBranch={onToggleBranch}
  116. onClearAllBranches={onClearAllBranches}
  117. isSubmitting={isSubmitting}
  118. />
  119. ) : null}
  120. </div>
  121. );
  122. }