Passed
Push — master ( 58a9e6...52bfcc )
by
unknown
02:37
created

LevelStorage   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 36
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 29
eloc 28
dl 0
loc 36
rs 10
c 0
b 0
f 0

8 Functions

Rating   Name   Duplication   Size   Complexity  
A setMaxLevel 0 3 1
A isBelowMax 0 3 1
A increase 0 3 1
A decrease 0 3 1
A getMaxLevel 0 3 1
A getLevel 0 3 1
A isAboveMin 0 3 1
A reset 0 4 1
1
import { event, select } from 'd3-selection';
2
import { DependencyNode, TreeNode } from '../../components/types';
3
import {
4
    centerScreenToDimension,
5
    changeZoom,
6
    findGroupBackgroundDimension,
7
    findMaxDependencyLevel,
8
    getHighLightedLabelColor,
9
    hideHighlightBackground,
10
    highlight,
11
    zoomToHighLightedNodes,
12
} from './GraphHelpers';
13
import { LabelColors, MAXIMUM_ZOOM_SCALE, MINIMUM_ZOOM_SCALE, ElementIds, TextColors, ZOOM_DECREASE, ZOOM_INCREASE } from '../AppConsts';
14
import { zoom, zoomIdentity } from 'd3-zoom';
15
import {
16
    selectAllNodes,
17
    selectContainer,
18
    selectDetailsButtonWrapper,
19
    selectDetailsExitButtonWrapper,
20
    selectHighLightedNodes,
21
} from './Selectors';
22
import { initializeDetailsView, shutdownDetailsView } from './DetailsDrawHelpers';
23
24
enum Subscriptions {
25
    HIGHLIGHT = 'click.highlight',
26
    RESET_HIGHLIGHT = 'click.resetHighlight',
27
    CHANGE_HIGHLIGHT_RANGE = 'keydown.changeHighlightRange',
28
    ZOOM_ON_ARROW_KEY = 'keydown.zoom',
29
    OPEN_DETAILS = 'click.openDetails',
30
    CLOSE_DETAILS = 'click.closeDetails',
31
}
32
33
export function subscribeToHighlight() {
34
    selectAllNodes().on(Subscriptions.HIGHLIGHT, (node: DependencyNode) => {
35
        LevelStorage.reset();
36
        if (node.links.length) {
37
            highlight(node);
38
            zoomToHighLightedNodes();
39
        }
40
        event.stopPropagation();
41
    });
42
}
43
44
export function subscribeToChangeHighlightRangeOnArrowKey() {
45
    select('body').on(Subscriptions.CHANGE_HIGHLIGHT_RANGE, () => {
46
        if (event.code === 'ArrowRight' || event.code === 'ArrowLeft') {
47
            const labelNodesGroup = select<SVGGElement, DependencyNode>('g#labels');
48
            LevelStorage.setMaxLevel(findMaxDependencyLevel(labelNodesGroup));
49
50
            if (!isFinite(LevelStorage.getMaxLevel())) {
51
                return;
52
            }
53
54
            if (LevelStorage.isBelowMax() && event.code === 'ArrowRight') {
55
                LevelStorage.increase();
56
            }
57
58
            if (LevelStorage.isAboveMin() && event.code === 'ArrowLeft') {
59
                LevelStorage.decrease();
60
            }
61
62
            // TODO refactor it to share logic with GraphHelpers/highlight function
63
            labelNodesGroup
64
                .selectAll<HTMLElement, DependencyNode>('g')
65
                .filter((node: DependencyNode) => node.level > 0)
66
                .each(function(this: HTMLElement, node: DependencyNode) {
67
                    const labelElement = this.firstElementChild;
68
                    const textElement = this.lastElementChild;
69
70
                    if (!labelElement || !textElement) {
71
                        return;
72
                    }
73
74
                    let labelColor = LabelColors.DEFAULT;
75
                    let textColor = TextColors.DEFAULT;
76
                    if (node.level - 1 <= LevelStorage.getLevel()) {
77
                        labelColor = getHighLightedLabelColor(node);
78
                        textColor = TextColors.HIGHLIGHTED;
79
                    }
80
81
                    select<Element, DependencyNode>(labelElement).attr('fill', labelColor);
82
                    select<Element, DependencyNode>(textElement).style('fill', textColor);
83
                });
84
85
            zoomToHighLightedNodes();
86
        }
87
    });
88
}
89
90
export function subscribeToResetHighlight() {
91
    selectContainer().on(Subscriptions.RESET_HIGHLIGHT, () => {
92
        const highlightedNodes = selectHighLightedNodes();
93
        if (highlightedNodes.data().length) {
94
            selectAllNodes().each((node: DependencyNode) => (node.level = 0));
95
96
            const dimension = findGroupBackgroundDimension(highlightedNodes.data());
97
98
            highlightedNodes.each(function(this: SVGGElement) {
99
                const labelElement = this.firstElementChild;
100
                const textElement = this.lastElementChild;
101
102
                if (!labelElement || !textElement) {
103
                    return;
104
                }
105
106
                select<Element, DependencyNode>(labelElement).attr('fill', LabelColors.DEFAULT);
107
                select<Element, DependencyNode>(textElement).style('fill', TextColors.DEFAULT);
108
            });
109
110
            hideHighlightBackground();
111
112
            centerScreenToDimension(dimension, 1);
113
        }
114
    });
115
}
116
117
export function subscribeToZoomOnArrowKey() {
118
    select('body').on(Subscriptions.ZOOM_ON_ARROW_KEY, () => {
119
        switch (event.code) {
120
            case 'ArrowUp': {
121
                const currentScaleValue = ZoomScaleStorage.getScale();
122
                const newScaleValue = currentScaleValue * ZOOM_INCREASE;
123
                if (newScaleValue <= MAXIMUM_ZOOM_SCALE) {
124
                    ZoomScaleStorage.setScale(newScaleValue);
125
                    const container = selectContainer();
126
                    container.call(() => {
127
                        return zoom<any, any>()
128
                            .on('zoom', changeZoom(ElementIds.ZOOM_OVERVIEW))
129
                            .scaleBy(container, ZOOM_INCREASE);
130
                    }, zoomIdentity);
131
                }
132
                break;
133
            }
134
            case 'ArrowDown': {
135
                const currentScaleValue = ZoomScaleStorage.getScale();
136
                const newScaleValue = currentScaleValue * ZOOM_DECREASE;
137
                if (newScaleValue >= MINIMUM_ZOOM_SCALE) {
138
                    ZoomScaleStorage.setScale(newScaleValue);
139
                    const container = selectContainer();
140
                    container.call(() => {
141
                        return zoom<any, any>()
142
                            .on('zoom', changeZoom(ElementIds.ZOOM_OVERVIEW))
143
                            .scaleBy(container, ZOOM_DECREASE);
144
                    }, zoomIdentity);
145
                }
146
                break;
147
            }
148
        }
149
    });
150
}
151
152
export function subscribeToOpenDetails(detailsNodes: TreeNode[]) {
153
    selectDetailsButtonWrapper().on(Subscriptions.OPEN_DETAILS, () => {
154
        if (selectHighLightedNodes().data().length === 0) {
155
            return;
156
        }
157
        event.stopPropagation();
158
        const selectedNode = selectAllNodes()
159
            .data()
160
            .find(node => node.level === 1);
161
        if (selectedNode) {
162
            const selectedTreeNode = detailsNodes.find(treeNode => treeNode.name === selectedNode.name);
163
            if (selectedTreeNode) {
164
                initializeDetailsView(selectedTreeNode);
165
            }
166
        }
167
    });
168
}
169
170
export function subscribeToCloseDetails() {
171
    selectDetailsExitButtonWrapper().on(Subscriptions.CLOSE_DETAILS, () => {
172
        shutdownDetailsView();
173
    });
174
}
175
176
class LevelStorage {
177
    private static level: number = 1;
178
    private static maxLevel: number = 1;
179
180
    public static getLevel(): number {
181
        return this.level;
182
    }
183
184
    public static increase() {
185
        this.level = this.level + 1;
186
    }
187
188
    public static decrease() {
189
        this.level = this.level - 1;
190
    }
191
192
    public static isBelowMax() {
193
        return this.level < this.maxLevel;
194
    }
195
196
    static isAboveMin() {
197
        return this.level > 1;
198
    }
199
200
    static setMaxLevel(maxLevel: number) {
201
        this.maxLevel = maxLevel;
202
    }
203
204
    static getMaxLevel(): number {
205
        return this.maxLevel;
206
    }
207
208
    public static reset() {
209
        this.level = 1;
210
        this.maxLevel = 1;
211
    }
212
}
213
214
export class ZoomScaleStorage {
215
    private static currentScale = 1;
216
217
    public static setScale(newScale: number) {
218
        this.currentScale = newScale;
219
    }
220
221
    public static getScale() {
222
        return this.currentScale;
223
    }
224
}
225