~sirn/fanboi2

ref: 5b482cfc1bfb0bb1e89715cb57c7bac864391768 fanboi2/assets/app/javascripts/components/board_selector.ts -rw-r--r-- 4.4 KiB
5b482cfcKridsada Thanabulpong 0.30.0 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
118
119
120
121
122
123
124
125
126
127
128
129
import {VNode, create, diff, h, patch} from 'virtual-dom';

import {SingletonComponent} from './base';
import {Board} from '../models/board';
import {BoardSelectorView} from '../views/board_selector_view';
import {addClass} from '../utils/elements';


const animationDuration = 200;


export class BoardSelector extends SingletonComponent {
    public targetSelector = '[data-board-selector]';

    protected bindOne($element: Element): void {
        let $button: Element;
        let $selector: Element | undefined;
        let selectorView: BoardSelectorView | undefined;
        let selectorNode: VNode | undefined;
        let selectorState: boolean = false;
        let throttleTimer: number;

        let _render = (height: number = 0): Promise<any> => {
            if (!$selector) {
                return Board.queryAll().then((boards: Board[]) => {
                    selectorView = new BoardSelectorView(boards);
                    selectorNode = selectorView.render();
                    $selector = create(selectorNode);
                    document.body.insertBefore($selector, $element.nextSibling);
                });
            } else {
                return new Promise((resolve) => {
                    resolve();
                });
            }
        }

        let _update = (height: number): void => {
            if ($selector && selectorView && selectorNode) {
                let newSelectorNode = selectorView.render({
                    style: {
                        height: `${height}px`,
                    }
                });

                let patches = diff(selectorNode, newSelectorNode);
                $selector = patch($selector, patches);
                selectorNode = newSelectorNode;
            }
        }

        let _animate = (updateFn: ((elapsedPercent: number) => void)) => {
            let startTime: number = 0;
            let _animateStep = (time: number) => {
                if (!startTime) {
                    startTime = time;
                }

                let elapsed = Math.min(time - startTime, animationDuration);
                let elapsedPercent = elapsed/animationDuration;

                updateFn(elapsedPercent);

                if (elapsed < animationDuration) {
                    requestAnimationFrame(_animateStep);
                }
            }

            requestAnimationFrame(_animateStep);
        }

        $button = create(h('div',
            {className: 'js-board-selector-button'},
            [h('a', {'href': '#'}, ['Boards'])]
        ));

        $button.addEventListener('click', (e) => {
            e.preventDefault();
            _render().then(() => {
                if ($selector) {
                    let selectorHeight = BoardSelector.getSelectorHeight(
                        $selector
                    );

                    if (selectorState) {
                        selectorState = false;
                        _animate((elapsedPercent: number) => {
                            _update(selectorHeight * (1 - elapsedPercent));
                        });
                    } else {
                        selectorState = true;
                        _animate((elapsedPercent: number) => {
                            _update(selectorHeight * elapsedPercent);
                        });
                    }
                }
            });
        });

        $element.querySelector('.container').appendChild($button);
        addClass($element, ['js-board-selector-wrapper']);

        // Attempt to restore height on resize. Since the resize may cause
        // clientHeight to change (and will cause the board selector to be
        // clipped or has extra whitespace).
        //
        // Do nothing if resize was called before board selector was attached
        // or if selector was attached but not displayed.
        window.addEventListener('resize', (e: Event) => {
            clearTimeout(throttleTimer);
            throttleTimer = setTimeout(() => {
                if ($selector && selectorState) {
                    _update(BoardSelector.getSelectorHeight($selector));
                    selectorState = true;
                }
            }, 100);
        });
    }

    private static getSelectorHeight($selector: Element): number {
        let $el = $selector.querySelector('.js-board-selector-inner');

        if ($el) {
            return $el.clientHeight;
        }

        throw new Error('Could not retrieve board selector height.');
    }
}