virtualizer.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { type VirtualFileMetrics, Virtualizer } from "@pierre/diffs"
  2. type Target = {
  3. key: Document | HTMLElement
  4. root: Document | HTMLElement
  5. content: HTMLElement | undefined
  6. }
  7. type Entry = {
  8. virtualizer: Virtualizer
  9. refs: number
  10. }
  11. const cache = new WeakMap<Document | HTMLElement, Entry>()
  12. export const virtualMetrics: Partial<VirtualFileMetrics> = {
  13. lineHeight: 24,
  14. hunkSeparatorHeight: 24,
  15. fileGap: 0,
  16. }
  17. function scrollable(value: string) {
  18. return value === "auto" || value === "scroll" || value === "overlay"
  19. }
  20. function scrollRoot(container: HTMLElement) {
  21. let node = container.parentElement
  22. while (node) {
  23. const style = getComputedStyle(node)
  24. if (scrollable(style.overflowY)) return node
  25. node = node.parentElement
  26. }
  27. }
  28. function target(container: HTMLElement): Target | undefined {
  29. if (typeof document === "undefined") return
  30. const review = container.closest("[data-component='session-review']")
  31. if (review instanceof HTMLElement) {
  32. const root = scrollRoot(container) ?? review
  33. const content = review.querySelector("[data-slot='session-review-container']")
  34. return {
  35. key: review,
  36. root,
  37. content: content instanceof HTMLElement ? content : undefined,
  38. }
  39. }
  40. const root = scrollRoot(container)
  41. if (root) {
  42. const content = root.querySelector("[role='log']")
  43. return {
  44. key: root,
  45. root,
  46. content: content instanceof HTMLElement ? content : undefined,
  47. }
  48. }
  49. return {
  50. key: document,
  51. root: document,
  52. content: undefined,
  53. }
  54. }
  55. export function acquireVirtualizer(container: HTMLElement) {
  56. const resolved = target(container)
  57. if (!resolved) return
  58. let entry = cache.get(resolved.key)
  59. if (!entry) {
  60. const virtualizer = new Virtualizer()
  61. virtualizer.setup(resolved.root, resolved.content)
  62. entry = {
  63. virtualizer,
  64. refs: 0,
  65. }
  66. cache.set(resolved.key, entry)
  67. }
  68. entry.refs += 1
  69. let done = false
  70. return {
  71. virtualizer: entry.virtualizer,
  72. release() {
  73. if (done) return
  74. done = true
  75. const current = cache.get(resolved.key)
  76. if (!current) return
  77. current.refs -= 1
  78. if (current.refs > 0) return
  79. current.virtualizer.cleanUp()
  80. cache.delete(resolved.key)
  81. },
  82. }
  83. }