Ben Lin
2024-07-01 f60c5156615626515bd6d84f151a1292b8b936c1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
 * Makes the first character of a string uppercase
 */
export function upperFirst(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}
 
interface HTMLExpandElement extends HTMLElement {
  _parent?: (Node & ParentNode & HTMLElement) | null;
  _initialStyle: {
    transition: string;
    overflow: string | null;
    height?: string | null;
    width?: string | null;
  };
}
 
export default function (expandedParentClass = '', x = false) {
  const sizeProperty = x ? 'width' : ('height' as 'width' | 'height');
  const offsetProperty = `offset${upperFirst(sizeProperty)}` as 'offsetHeight' | 'offsetWidth';
 
  return {
    beforeEnter(el: HTMLExpandElement) {
      el._parent = el.parentNode as (Node & ParentNode & HTMLElement) | null;
      el._initialStyle = {
        transition: el.style.transition,
        overflow: el.style.overflow,
        [sizeProperty]: el.style[sizeProperty],
      };
    },
 
    enter(el: HTMLExpandElement) {
      const initialStyle = el._initialStyle;
 
      el.style.setProperty('transition', 'none', 'important');
      el.style.overflow = 'hidden';
      // const offset = `${el[offsetProperty]}px`;
 
      // el.style[sizeProperty] = '0';
 
      void el.offsetHeight; // force reflow
 
      el.style.transition = initialStyle.transition;
 
      if (expandedParentClass && el._parent) {
        el._parent.classList.add(expandedParentClass);
      }
 
      requestAnimationFrame(() => {
        // el.style[sizeProperty] = offset;
      });
    },
 
    afterEnter: resetStyles,
    enterCancelled: resetStyles,
 
    leave(el: HTMLExpandElement) {
      el._initialStyle = {
        transition: '',
        overflow: el.style.overflow,
        [sizeProperty]: el.style[sizeProperty],
      };
 
      el.style.overflow = 'hidden';
      el.style[sizeProperty] = `${el[offsetProperty]}px`;
      /* eslint-disable-next-line */
      void el.offsetHeight; // force reflow
 
      requestAnimationFrame(() => (el.style[sizeProperty] = '0'));
    },
 
    afterLeave,
    leaveCancelled: afterLeave,
  };
 
  function afterLeave(el: HTMLExpandElement) {
    if (expandedParentClass && el._parent) {
      el._parent.classList.remove(expandedParentClass);
    }
    resetStyles(el);
  }
 
  function resetStyles(el: HTMLExpandElement) {
    const size = el._initialStyle[sizeProperty];
    el.style.overflow = el._initialStyle.overflow!;
    if (size != null) el.style[sizeProperty] = size;
    Reflect.deleteProperty(el, '_initialStyle');
  }
}