tabs-container.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { useFileTreeOpenContext } from '@/features/ide-react/context/file-tree-open-context'
  2. import {
  3. TAB_TRANSFER_TYPE,
  4. useTabsContext,
  5. } from '@/features/ide-react/context/tabs-context'
  6. import { Tab } from './tab'
  7. import SplitTestBadge from '@/shared/components/split-test-badge'
  8. import { useCallback, useMemo, useState } from 'react'
  9. import { throttle } from 'lodash'
  10. import { debugConsole } from '@/utils/debugging'
  11. import classNames from 'classnames'
  12. export const TabsContainer = () => {
  13. const { tabs, openTab, closeTab, moveTab, makeTabPermanent } =
  14. useTabsContext()
  15. const { openEntity } = useFileTreeOpenContext()
  16. const [hovered, setHovered] = useState<boolean>(false)
  17. const throttledOnDragOver = useMemo(
  18. () =>
  19. throttle(() => {
  20. setHovered(true)
  21. }, 50),
  22. []
  23. )
  24. const onDragOver = useCallback(
  25. (e: React.DragEvent) => {
  26. e.preventDefault()
  27. throttledOnDragOver()
  28. },
  29. [throttledOnDragOver]
  30. )
  31. const onDrop = useCallback(
  32. (e: React.DragEvent) => {
  33. throttledOnDragOver.cancel()
  34. e.stopPropagation()
  35. e.preventDefault()
  36. setHovered(false)
  37. const draggedTabId = e.dataTransfer.getData(TAB_TRANSFER_TYPE)
  38. if (!draggedTabId) {
  39. debugConsole.warn('No dragged tab id found in dataTransfer')
  40. return
  41. }
  42. const targetTabId = tabs[tabs.length - 1]?.id
  43. if (!targetTabId) {
  44. debugConsole.warn('No target tab id found for drop')
  45. return
  46. }
  47. moveTab(draggedTabId, targetTabId, 'right')
  48. },
  49. [tabs, moveTab, throttledOnDragOver]
  50. )
  51. const onDragLeave = useCallback(() => {
  52. throttledOnDragOver.cancel()
  53. setHovered(false)
  54. }, [throttledOnDragOver])
  55. const onWheel = useCallback((e: React.WheelEvent) => {
  56. if (e.deltaY !== 0 && e.deltaX === 0) {
  57. // if this is a purely vertical scroll, convert it to a horizontal scroll
  58. // instead
  59. e.currentTarget.scrollLeft += e.deltaY
  60. }
  61. }, [])
  62. return (
  63. <div className="editor-tabs-container">
  64. <div
  65. className={classNames('editor-tabs-row', {
  66. 'editor-tabs-row-hovered': hovered,
  67. })}
  68. role="tablist"
  69. onDragOver={onDragOver}
  70. onDrop={onDrop}
  71. onDragLeave={onDragLeave}
  72. onWheel={onWheel}
  73. tabIndex={-1}
  74. >
  75. {tabs.map(tab => (
  76. <Tab
  77. key={tab.id}
  78. tab={tab}
  79. openTab={openTab}
  80. closeTab={closeTab}
  81. isSelected={openEntity?.entity._id === tab.id}
  82. onTabDrop={moveTab}
  83. makeTabPermanent={makeTabPermanent}
  84. />
  85. ))}
  86. </div>
  87. <div className="editor-tabs-labs-icon">
  88. <SplitTestBadge
  89. splitTestName="editor-tabs"
  90. displayOnVariants={['enabled']}
  91. />
  92. </div>
  93. </div>
  94. )
  95. }