aboutsummaryrefslogtreecommitdiff
path: root/src/lib/Utility/html.ts
blob: ccdfae8d7a6754793ba3a6a51ac8b0b822a513be (plain) (blame)
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
90
91
import settings from '$stores/settings';
import { get } from 'svelte/store';

export const nbsp = (str: string) => str.replace(/ /g, ' ');

export const createHeightObserver = (details = true) => {
  const observedElements = new Set<HTMLElement>();
  const resizeObservers = new Map<HTMLElement, ResizeObserver>();
  const detailObservers = new Map<HTMLElement, MutationObserver>();

  const applyHeightLimit = (target: HTMLElement) => {
    if (!get(settings).displayLimitListHeight) {
      target.style.height = 'auto';

      return;
    }

    target.style.height = 'auto';

    const elementBound = target.getBoundingClientRect();
    const height = window.innerHeight - elementBound.top - 2.5 * 16;

    if (elementBound.height > height) target.style.height = `${height}px`;
  };

  const observeElement = (element: HTMLElement) => {
    if (element.dataset.observed) return;

    const resizeObserver = new ResizeObserver((entries) => {
      entries.forEach((entry) => {
        const target = entry.target as HTMLElement;

        applyHeightLimit(target);
      });
    });

    resizeObserver.observe(element);
    resizeObservers.set(element, resizeObserver);

    if (details) {
      const detailsObserver = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          const target = mutation.target as HTMLDetailsElement;

          if (target.tagName === 'DETAILS' && !target.open) target.style.height = 'auto';
        });
      });

      detailsObserver.observe(element, { attributes: true });
      detailObservers.set(element, detailsObserver);
    }

    element.dataset.observed = 'true';

    observedElements.add(element);
    applyHeightLimit(element);
  };

  document.querySelectorAll<HTMLElement>('.list').forEach(observeElement);

  const mutationObserver = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      mutation.addedNodes.forEach((node) => {
        if (!(node instanceof HTMLElement)) return;

        if (node.matches('.list')) observeElement(node);

        node.querySelectorAll<HTMLElement>('.list').forEach(observeElement);
      });
    });
  });

  mutationObserver.observe(document.body, { childList: true, subtree: true });

  const unsubscribeSettings = settings.subscribe(() => {
    observedElements.forEach((element) => applyHeightLimit(element));
  });

  return () => {
    unsubscribeSettings();
    mutationObserver.disconnect();
    resizeObservers.forEach((observer) => observer.disconnect());
    detailObservers.forEach((observer) => observer.disconnect());

    observedElements.forEach((element) => {
      element.style.height = 'auto';

      delete element.dataset.observed;
    });
  };
};