~sirn/fanboi2

ref: a68873a108d20a6a49a1b6caf374bfb2cf6a94fe fanboi2/assets/app/javascripts/views/popover_view.ts -rw-r--r-- 3.3 KiB
a68873a1Kridsada Thanabulpong Coding style cleanups and setup pre-commit hooks (#42) 3 years ago
                                                                                
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { VNode, h } from "virtual-dom";

export class PopoverView {
    popoverChildNodes: VNode[];

    constructor(childNode: VNode, title?: string, dismissFn?: (() => void)) {
        this.popoverChildNodes = PopoverView.renderChild(childNode, title, dismissFn);
    }

    render(targetElement: Element, args: any = {}): VNode {
        let pos = PopoverView.computePosition(targetElement);

        return h("div", PopoverView.getViewClassName(args), [
            h(
                "div",
                {
                    className: "js-popover-inner",
                    style: {
                        position: "absolute",
                        top: `${pos.posX}px`,
                        left: `${pos.posY}px`,
                    },
                },
                this.popoverChildNodes,
            ),
        ]);
    }

    private static renderChild(
        childNode: VNode,
        title?: string,
        dismissFn?: (() => void),
    ): VNode[] {
        let popoverChildNodes: VNode[] = [];

        if (title) {
            let titleChildNodes = [
                h(
                    "span",
                    {
                        className: "js-popover-inner-title-label",
                    },
                    [title],
                ),
            ];

            if (dismissFn) {
                titleChildNodes.push(
                    h(
                        "a",
                        {
                            className: "js-popover-inner-title-dismiss",
                            href: "#",
                            onclick: (e: Event): void => {
                                e.preventDefault();
                                if (dismissFn) {
                                    dismissFn();
                                }
                            },
                        },
                        ["Close"],
                    ),
                );
            }

            popoverChildNodes.push(
                h(
                    "div",
                    {
                        className: "js-popover-inner-title",
                    },
                    titleChildNodes,
                ),
            );
        }

        popoverChildNodes.push(childNode);
        return popoverChildNodes;
    }

    private static computePosition(
        targetElement: Element,
    ): {
        posX: number;
        posY: number;
    } {
        let bodyRect = document.body.getBoundingClientRect();
        let elemRect = targetElement.getBoundingClientRect();
        let yRefRect = elemRect;

        // Indent relative to container rather than element if there is
        // container in element ancestor.
        let containerElement = targetElement.closest(".container");
        if (containerElement) {
            yRefRect = containerElement.getBoundingClientRect();
        }

        return {
            posX: elemRect.bottom + 5 - bodyRect.top,
            posY: yRefRect.left - bodyRect.left,
        };
    }

    private static getViewClassName(args: any): any {
        const className = "js-popover";

        if (args.className) {
            let classNames = args.className.split(" ");
            classNames.push(className);
            args.className = classNames.join(" ");
        } else {
            args.className = className;
        }

        return args;
    }
}