ProjectBranchSelectorSheet.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { Box, Boxes, GitBranch } from 'lucide-react'
  2. import {
  3. cn,
  4. SheetHeader,
  5. SheetTitle,
  6. Tabs_Shadcn_,
  7. TabsContent_Shadcn_,
  8. TabsList_Shadcn_,
  9. } from 'ui'
  10. import MobileSheetNav from 'ui-patterns/MobileSheetNav/MobileSheetNav'
  11. import { ProjectBranchSelectorSheetTabTrigger } from './ProjectBranchSelectorSheetTabTrigger'
  12. import { BranchDropdown } from '@/components/layouts/AppLayout/BranchDropdown'
  13. import { OrganizationDropdown } from '@/components/layouts/AppLayout/OrganizationDropdown'
  14. import { ProjectDropdown } from '@/components/layouts/AppLayout/ProjectDropdown'
  15. import type { Branch } from '@/data/branches/branches-query'
  16. import type { ProjectDetail } from '@/data/projects/project-detail-query'
  17. import type { Organization } from '@/types'
  18. const EMBEDDED_CLASSNAME =
  19. 'bg-transparent border-0 shadow-none min-h-0 flex-1 flex flex-col overflow-hidden rounded-none'
  20. export interface ProjectBranchSelectorSheetProps {
  21. open: boolean
  22. onOpenChange: (open: boolean) => void
  23. selectedRef: string | undefined
  24. onClose: () => void
  25. selectedOrganization?: Organization | null
  26. displayProject?: ProjectDetail | null
  27. selectedBranch?: Branch | null
  28. selectedOrganizationName?: string | null
  29. selectedProjectName?: string | null
  30. selectedBranchName?: string | null
  31. isMainBranch?: boolean
  32. }
  33. export function ProjectBranchSelectorSheet({
  34. open,
  35. onOpenChange,
  36. selectedRef,
  37. onClose,
  38. selectedOrganization,
  39. displayProject,
  40. selectedBranch,
  41. selectedOrganizationName,
  42. selectedProjectName,
  43. selectedBranchName,
  44. isMainBranch,
  45. }: ProjectBranchSelectorSheetProps) {
  46. const orgLabel = selectedOrganizationName ?? selectedOrganization?.name
  47. const projectLabel = selectedProjectName ?? displayProject?.name
  48. const branchLabel = selectedBranchName ?? selectedBranch?.name ?? 'main'
  49. const tabs = [
  50. orgLabel && {
  51. value: 'organization' as const,
  52. label: orgLabel,
  53. icon: Boxes,
  54. },
  55. projectLabel && {
  56. value: 'project' as const,
  57. label: projectLabel,
  58. icon: Box,
  59. },
  60. branchLabel && {
  61. value: 'branch' as const,
  62. label: branchLabel,
  63. icon: GitBranch,
  64. },
  65. ].filter(Boolean) as Array<{
  66. value: 'organization' | 'project' | 'branch'
  67. label: string
  68. icon: typeof Boxes
  69. }>
  70. const defaultTab =
  71. (selectedRef && tabs.some((t) => t.value === 'project') ? 'project' : tabs[0]?.value) ??
  72. 'organization'
  73. if (tabs.length === 0) {
  74. return null
  75. }
  76. return (
  77. <MobileSheetNav
  78. open={open}
  79. onOpenChange={onOpenChange}
  80. className="flex flex-col overflow-hidden h-[85dvh] md:max-h-[500px]"
  81. >
  82. <Tabs_Shadcn_
  83. defaultValue={defaultTab}
  84. className="flex flex-col flex-1 min-h-0 overflow-hidden p-0"
  85. >
  86. <SheetHeader className="border-0 border-default p-0 shrink-0">
  87. <SheetTitle className="sr-only">Switch organization, project or branch</SheetTitle>
  88. <TabsList_Shadcn_
  89. className={cn(
  90. 'w-full grid gap-0 shrink-0',
  91. tabs.length === 1 && 'grid-cols-1',
  92. tabs.length === 2 && 'grid-cols-2',
  93. tabs.length === 3 && 'grid-cols-3'
  94. )}
  95. >
  96. {tabs.map(({ value, label, icon }, index) => (
  97. <ProjectBranchSelectorSheetTabTrigger
  98. key={value}
  99. value={value}
  100. label={label}
  101. icon={icon}
  102. isMainBranch={value === 'branch' ? isMainBranch : undefined}
  103. isLast={index === tabs.length - 1}
  104. />
  105. ))}
  106. </TabsList_Shadcn_>
  107. </SheetHeader>
  108. {tabs.some((t) => t.value === 'organization') && (
  109. <TabsContent_Shadcn_
  110. value="organization"
  111. className="flex-1 min-h-0 overflow-hidden flex flex-col mt-0 p-0 data-[state=inactive]:hidden"
  112. >
  113. <OrganizationDropdown embedded className={EMBEDDED_CLASSNAME} onClose={onClose} />
  114. </TabsContent_Shadcn_>
  115. )}
  116. {tabs.some((t) => t.value === 'project') && (
  117. <TabsContent_Shadcn_
  118. value="project"
  119. className="flex-1 min-h-0 overflow-hidden flex flex-col mt-0 py-0 data-[state=inactive]:hidden"
  120. >
  121. <ProjectDropdown embedded className={EMBEDDED_CLASSNAME} onClose={onClose} />
  122. </TabsContent_Shadcn_>
  123. )}
  124. {tabs.some((t) => t.value === 'branch') && (
  125. <TabsContent_Shadcn_
  126. value="branch"
  127. className="flex-1 min-h-0 overflow-hidden flex flex-col mt-0 p-0 data-[state=inactive]:hidden"
  128. >
  129. <BranchDropdown embedded className={EMBEDDED_CLASSNAME} onClose={onClose} />
  130. </TabsContent_Shadcn_>
  131. )}
  132. </Tabs_Shadcn_>
  133. </MobileSheetNav>
  134. )
  135. }