Tabs.utils.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { useCallback, useEffect, useRef } from 'react'
  2. import { Entity } from '@/data/entity-types/entity-types-infinite-query'
  3. import useLatest from '@/hooks/misc/useLatest'
  4. import { createTabId, editorEntityTypes, useTabsStateSnapshot } from '@/state/tabs'
  5. export function useTableEditorTabsCleanUp() {
  6. const tabs = useTabsStateSnapshot()
  7. const tabMapRef = useLatest(tabs.tabsMap)
  8. const recentItemsRef = useLatest(tabs.recentItems)
  9. const openTabsRef = useLatest(tabs.openTabs)
  10. return useCallback(({ schemas, entities }: { schemas: string[]; entities: Entity[] }) => {
  11. // Identity all entities by tab ID
  12. const entitiesById: string[] = entities.map((x: Entity) => createTabId(x.type, { id: x.id }))
  13. const recentItemsFilteredToSchemas: string[] = []
  14. for (const schema of schemas) {
  15. recentItemsFilteredToSchemas.push(
  16. ...recentItemsRef.current.filter((x) => x.metadata?.schema === schema).map((x) => x.id)
  17. )
  18. }
  19. const recentItemsToRemove = [
  20. ...recentItemsFilteredToSchemas.filter((entityId) => !entitiesById.includes(entityId)),
  21. ]
  22. tabs.removeRecentItems(recentItemsToRemove)
  23. const tabsFilteredToSchemas: string[] = []
  24. for (const schema of schemas) {
  25. tabsFilteredToSchemas.push(
  26. ...openTabsRef.current.filter((tabId) => {
  27. const tab = tabMapRef.current[tabId]
  28. return tab?.metadata?.schema === schema
  29. })
  30. )
  31. }
  32. const tableEditorTabsToBeCleaned = [
  33. ...tabsFilteredToSchemas.filter(
  34. (id) => !id.startsWith('sql') && !id.startsWith('schema') && !entitiesById.includes(id)
  35. ),
  36. ]
  37. // perform tabs cleanup
  38. tabs.removeTabs(tableEditorTabsToBeCleaned)
  39. // [Joshen] Validate for opened tabs, if their label matches the entity's name - update label if not
  40. // As the entity could've been renamed outside of the table editor
  41. // e.g Using the SQL editor to rename the entity
  42. const openTabs = openTabsRef.current
  43. .map((id) => tabMapRef.current[id])
  44. .filter((tab) => !!tab && editorEntityTypes['table']?.includes(tab.type))
  45. openTabs.forEach((tab) => {
  46. const entity = entities?.find((x) => tab.metadata?.tableId === x.id)
  47. if (!!entity && entity.name !== tab.label) tabs.updateTab(tab.id, { label: entity.name })
  48. })
  49. }, [])
  50. }
  51. export function useSqlEditorTabsCleanup() {
  52. const tabs = useTabsStateSnapshot()
  53. const tabMapRef = useLatest(tabs.tabsMap)
  54. const openTabsRef = useLatest(tabs.openTabs)
  55. return useCallback(({ snippets }: { snippets: { id: string; type: string; name: string }[] }) => {
  56. // these are tabs that are static content
  57. // these canot be removed from localstorage based on this query request
  58. const IGNORED_TAB_IDS = ['sql-templates', 'sql-quickstarts']
  59. // Identify all SQL snippets / content by their tab ids
  60. const currentContentIds = [
  61. ...snippets
  62. .filter((content) => content.type === 'sql')
  63. .map((content) => createTabId('sql', { id: content.id })),
  64. // append ignored tab IDs
  65. ...IGNORED_TAB_IDS,
  66. ]
  67. // Remove any snippet tabs that might no longer be existing (removed outside of the dashboard session)
  68. const snippetTabsToBeCleaned = openTabsRef.current.filter(
  69. (id: string) => id.startsWith('sql') && !currentContentIds.includes(id)
  70. )
  71. tabs.removeTabs(snippetTabsToBeCleaned)
  72. // Remove any recent items that might no longer be existing (removed outside of the dashboard session)
  73. const recentItems = tabs.getRecentItemsByType('sql')
  74. tabs.removeRecentItems(
  75. recentItems
  76. ? recentItems.filter((item) => !currentContentIds.includes(item.id)).map((item) => item.id)
  77. : []
  78. )
  79. // [Joshen] Validate for opened tabs, if their label matches the snippet's name - update label if not
  80. // As the snippets name could've been updated outside of the SQL Editor session
  81. // e.g for a shared snippet, the owner could've updated the name of the snippet
  82. const openSqlTabs = openTabsRef.current
  83. .map((id) => tabMapRef.current[id])
  84. .filter((tab) => !!tab && editorEntityTypes['sql']?.includes(tab.type))
  85. openSqlTabs.forEach((tab) => {
  86. const snippet = snippets?.find((x) => tab.metadata?.sqlId === x.id)
  87. if (!!snippet && snippet.name !== tab.label) tabs.updateTab(tab.id, { label: snippet.name })
  88. })
  89. }, [])
  90. }
  91. interface UseTabsScrollOptions {
  92. activeTab: string | null | undefined
  93. tabCount: number
  94. }
  95. export function useTabsScroll({ activeTab, tabCount }: UseTabsScrollOptions) {
  96. const tabsListRef = useRef<HTMLDivElement>(null)
  97. const prevTabCountRef = useRef<number>(tabCount)
  98. const isInitialMount = useRef(true)
  99. useEffect(() => {
  100. if (tabsListRef.current) {
  101. tabsListRef.current.scrollLeft = tabsListRef.current.scrollWidth
  102. }
  103. }, [])
  104. useEffect(() => {
  105. if (isInitialMount.current) {
  106. isInitialMount.current = false
  107. return
  108. }
  109. if (!tabsListRef.current) return
  110. const tabCountIncreased = tabCount > prevTabCountRef.current
  111. if (tabCountIncreased) {
  112. tabsListRef.current.scrollLeft = tabsListRef.current.scrollWidth
  113. } else if (activeTab) {
  114. const activeTabElement = tabsListRef.current.querySelector(
  115. `[data-state="active"]`
  116. ) as HTMLElement
  117. if (activeTabElement) {
  118. activeTabElement.scrollIntoView({
  119. behavior: 'smooth',
  120. block: 'nearest',
  121. inline: 'nearest',
  122. })
  123. }
  124. }
  125. prevTabCountRef.current = tabCount
  126. }, [activeTab, tabCount])
  127. return { tabsListRef }
  128. }