codecharta-visualization 1.86.0 → 1.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +1 -1
- package/app/app.module.ts +6 -2
- package/app/codeCharta/codeCharta.model.ts +1 -1
- package/app/codeCharta/state/store/dynamicSettings/dynamicSettings.splitter.ts +2 -2
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/currentFocused.selector.ts +8 -0
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.actions.ts +27 -7
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.reducer.spec.ts +27 -11
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.reducer.ts +11 -5
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.selector.ts +4 -0
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.service.spec.ts +2 -2
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.service.ts +1 -1
- package/app/codeCharta/state/store/fileSettings/markedPackages/markedPackages.actions.ts +9 -10
- package/app/codeCharta/state/store/fileSettings/markedPackages/markedPackages.reducer.spec.ts +37 -7
- package/app/codeCharta/state/store/fileSettings/markedPackages/markedPackages.reducer.ts +7 -3
- package/app/codeCharta/state/store/fileSettings/markedPackages/util/findIndexOfMarkedPackageOrParent.spec.ts +39 -0
- package/app/codeCharta/state/store/fileSettings/markedPackages/util/findIndexOfMarkedPackageOrParent.ts +19 -0
- package/app/codeCharta/state/store/state.splitter.spec.ts +4 -4
- package/app/codeCharta/state/store.service.spec.ts +3 -3
- package/app/codeCharta/state/store.service.ts +5 -0
- package/app/codeCharta/ui/artificialIntelligence/__snapshots__/artificialIntelligence.component.spec.ts.snap +13 -16
- package/app/codeCharta/ui/artificialIntelligence/artificialIntelligence.component.html +4 -4
- package/app/codeCharta/ui/artificialIntelligence/artificialIntelligence.component.scss +0 -4
- package/app/codeCharta/ui/artificialIntelligence/artificialIntelligence.component.spec.ts +82 -52
- package/app/codeCharta/ui/artificialIntelligence/artificialIntelligence.component.ts +41 -101
- package/app/codeCharta/ui/codeMap/codeMap.actions.service.ts +1 -40
- package/app/codeCharta/ui/codeMap/codeMap.mouseEvent.service.ts +10 -16
- package/app/codeCharta/ui/colorPicker/colorPicker.component.html +16 -7
- package/app/codeCharta/ui/colorPicker/colorPicker.component.spec.ts +55 -16
- package/app/codeCharta/ui/colorPicker/colorPicker.component.ts +32 -1
- package/app/codeCharta/ui/colorPicker/colorPicker.module.ts +3 -1
- package/app/codeCharta/ui/customConfigs/customConfigs.component.html +21 -3
- package/app/codeCharta/ui/customConfigs/customConfigs.component.scss +1 -0
- package/app/codeCharta/ui/customConfigs/customConfigs.component.spec.ts +123 -2
- package/app/codeCharta/ui/customConfigs/customConfigs.component.ts +56 -2
- package/app/codeCharta/ui/labelledColorPicker/labelledColorPicker.component.html +21 -13
- package/app/codeCharta/ui/labelledColorPicker/labelledColorPicker.component.spec.ts +11 -24
- package/app/codeCharta/ui/labelledColorPicker/labelledColorPicker.component.ts +1 -40
- package/app/codeCharta/ui/labelledColorPicker/labelledColorPicker.module.ts +1 -2
- package/app/codeCharta/ui/legendPanel/legendMarkedPackages/legendMarkedPackages.component.ts +2 -2
- package/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.html +24 -0
- package/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.spec.ts +55 -0
- package/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.component.ts +38 -0
- package/app/codeCharta/ui/nodeContextMenu/focusButtons/focusButtons.module.ts +13 -0
- package/app/codeCharta/ui/nodeContextMenu/focusButtons/isNodeFocused.pipe.spec.ts +31 -0
- package/app/codeCharta/ui/nodeContextMenu/focusButtons/isNodeFocused.pipe.ts +25 -0
- package/app/codeCharta/ui/nodeContextMenu/markFolderColorPicker/markFolderColorPicker.component.html +8 -4
- package/app/codeCharta/ui/nodeContextMenu/markFolderColorPicker/markFolderColorPicker.component.ts +1 -21
- package/app/codeCharta/ui/nodeContextMenu/markFolderColorPicker/markFolderColorPicker.module.ts +1 -2
- package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.html +2 -12
- package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.scss +3 -1
- package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.spec.ts +28 -125
- package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.component.ts +29 -55
- package/app/codeCharta/ui/nodeContextMenu/nodeContextMenu.module.ts +2 -0
- package/app/codeCharta/ui/ui.ts +0 -2
- package/app/codeCharta/util/algorithm/streetLayout/strip/strip.ts +2 -2
- package/app/codeCharta/util/algorithm/streetLayout/treeMap.ts +1 -1
- package/app/codeCharta/util/algorithm/treeMapLayout/treeMapGenerator.spec.ts +1 -1
- package/app/codeCharta/util/algorithm/treeMapLayout/treeMapHelper.spec.ts +2 -2
- package/app/codeCharta/util/algorithm/treeMapLayout/treeMapHelper.ts +1 -1
- package/app/codeCharta/util/customConfigBuilder.ts +1 -1
- package/app/codeCharta/util/dataMocks.ts +2 -2
- package/app/codeCharta/util/fileExtensionCalculator.ts +1 -1
- package/dist/webpack/bundle.js +963 -934
- package/dist/webpack/bundle.js.map +1 -1
- package/package.json +6 -5
- package/app/codeCharta/state/store/dynamicSettings/focusedNodePath/focusedNodePath.splitter.ts +0 -8
- package/app/codeCharta/ui/codeMap/codeMap.actions.service.spec.ts +0 -147
- package/app/codeCharta/ui/unfocusButton/unfocusButton.component.html +0 -15
- package/app/codeCharta/ui/unfocusButton/unfocusButton.component.scss +0 -5
- package/app/codeCharta/ui/unfocusButton/unfocusButton.component.spec.ts +0 -106
- package/app/codeCharta/ui/unfocusButton/unfocusButton.component.ts +0 -62
- package/app/codeCharta/ui/unfocusButton/unfocusButton.module.ts +0 -7
|
@@ -2,27 +2,26 @@ import "./artificialIntelligence.module"
|
|
|
2
2
|
import { instantiateModule, getService } from "../../../../mocks/ng.mockhelper"
|
|
3
3
|
import { IRootScopeService } from "angular"
|
|
4
4
|
import { StoreService } from "../../state/store.service"
|
|
5
|
-
import { ThreeOrbitControlsService } from "../codeMap/threeViewer/threeOrbitControlsService"
|
|
6
|
-
import { ThreeCameraService } from "../codeMap/threeViewer/threeCameraService"
|
|
7
5
|
import { ArtificialIntelligenceController } from "./artificialIntelligence.component"
|
|
8
6
|
import { FilesService } from "../../state/store/files/files.service"
|
|
9
|
-
import { CustomConfig } from "../../model/customConfig/customConfig.api.model"
|
|
10
|
-
import { CustomConfigHelper } from "../../util/customConfigHelper"
|
|
11
7
|
import { setFiles } from "../../state/store/files/files.actions"
|
|
12
|
-
import { FILE_STATES, FILE_STATES_JAVA, FILE_STATES_UNSELECTED } from "../../util/dataMocks"
|
|
13
|
-
import { setState } from "../../state/store/state.actions"
|
|
8
|
+
import { FILE_STATES, FILE_STATES_JAVA, FILE_STATES_UNSELECTED, STATE } from "../../util/dataMocks"
|
|
14
9
|
import { klona } from "klona"
|
|
15
|
-
import { BlacklistType } from "../../codeCharta.model"
|
|
10
|
+
import { BlacklistType, ColorRange } from "../../codeCharta.model"
|
|
16
11
|
import { BlacklistService } from "../../state/store/fileSettings/blacklist/blacklist.service"
|
|
17
12
|
import { ExperimentalFeaturesEnabledService } from "../../state/store/appSettings/enableExperimentalFeatures/experimentalFeaturesEnabled.service"
|
|
18
13
|
import { setExperimentalFeaturesEnabled } from "../../state/store/appSettings/enableExperimentalFeatures/experimentalFeaturesEnabled.actions"
|
|
14
|
+
import { setColorRange } from "../../state/store/dynamicSettings/colorRange/colorRange.actions"
|
|
15
|
+
import { setColorMetric } from "../../state/store/dynamicSettings/colorMetric/colorMetric.actions"
|
|
16
|
+
import { setHeightMetric } from "../../state/store/dynamicSettings/heightMetric/heightMetric.actions"
|
|
17
|
+
import { setAreaMetric } from "../../state/store/dynamicSettings/areaMetric/areaMetric.actions"
|
|
18
|
+
import { setMapColors } from "../../state/store/appSettings/mapColors/mapColors.actions"
|
|
19
|
+
import { setState } from "../../state/store/state.actions"
|
|
19
20
|
|
|
20
21
|
describe("ArtificialIntelligenceController", () => {
|
|
21
22
|
let artificialIntelligenceController: ArtificialIntelligenceController
|
|
22
23
|
let $rootScope: IRootScopeService
|
|
23
24
|
let storeService: StoreService
|
|
24
|
-
let threeOrbitControlsService: ThreeOrbitControlsService
|
|
25
|
-
let threeCameraService: ThreeCameraService
|
|
26
25
|
|
|
27
26
|
beforeEach(() => {
|
|
28
27
|
restartSystem()
|
|
@@ -34,19 +33,12 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
34
33
|
|
|
35
34
|
$rootScope = getService<IRootScopeService>("$rootScope")
|
|
36
35
|
storeService = getService<StoreService>("storeService")
|
|
37
|
-
threeOrbitControlsService = getService<ThreeOrbitControlsService>("storeService")
|
|
38
|
-
threeCameraService = getService<ThreeCameraService>("storeService")
|
|
39
36
|
|
|
40
37
|
storeService.dispatch(setFiles(FILE_STATES))
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
function rebuildController() {
|
|
44
|
-
artificialIntelligenceController = new ArtificialIntelligenceController(
|
|
45
|
-
$rootScope,
|
|
46
|
-
storeService,
|
|
47
|
-
threeOrbitControlsService,
|
|
48
|
-
threeCameraService
|
|
49
|
-
)
|
|
41
|
+
artificialIntelligenceController = new ArtificialIntelligenceController($rootScope, storeService)
|
|
50
42
|
|
|
51
43
|
// Overwrite debounce with original function, otherwise calculate() will not be called
|
|
52
44
|
artificialIntelligenceController["debounceCalculation"] = artificialIntelligenceController["calculate"]
|
|
@@ -83,7 +75,7 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
83
75
|
artificialIntelligenceController["getMostFrequentLanguage"] = jest.fn()
|
|
84
76
|
artificialIntelligenceController["clearRiskProfile"] = jest.fn()
|
|
85
77
|
artificialIntelligenceController["calculateRiskProfile"] = jest.fn()
|
|
86
|
-
artificialIntelligenceController["
|
|
78
|
+
artificialIntelligenceController["calculateSuspiciousMetrics"] = jest.fn()
|
|
87
79
|
artificialIntelligenceController["fileState"] = FILE_STATES_JAVA[0]
|
|
88
80
|
|
|
89
81
|
storeService.dispatch(setExperimentalFeaturesEnabled(false))
|
|
@@ -92,14 +84,14 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
92
84
|
expect(artificialIntelligenceController["getMostFrequentLanguage"]).not.toHaveBeenCalled()
|
|
93
85
|
expect(artificialIntelligenceController["clearRiskProfile"]).not.toHaveBeenCalled()
|
|
94
86
|
expect(artificialIntelligenceController["calculateRiskProfile"]).not.toHaveBeenCalled()
|
|
95
|
-
expect(artificialIntelligenceController["
|
|
87
|
+
expect(artificialIntelligenceController["calculateSuspiciousMetrics"]).not.toHaveBeenCalled()
|
|
96
88
|
})
|
|
97
89
|
|
|
98
90
|
it("should calculate suspicious metrics when experimental features are enabled", function () {
|
|
99
91
|
artificialIntelligenceController["getMostFrequentLanguage"] = jest.fn().mockReturnValue("java")
|
|
100
92
|
artificialIntelligenceController["clearRiskProfile"] = jest.fn()
|
|
101
93
|
artificialIntelligenceController["calculateRiskProfile"] = jest.fn()
|
|
102
|
-
artificialIntelligenceController["
|
|
94
|
+
artificialIntelligenceController["calculateSuspiciousMetrics"] = jest.fn()
|
|
103
95
|
artificialIntelligenceController["fileState"] = FILE_STATES_JAVA[0]
|
|
104
96
|
|
|
105
97
|
storeService.dispatch(setExperimentalFeaturesEnabled(true))
|
|
@@ -109,28 +101,72 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
109
101
|
expect(artificialIntelligenceController["_viewModel"].analyzedProgrammingLanguage).toBe("java")
|
|
110
102
|
expect(artificialIntelligenceController["clearRiskProfile"]).toHaveBeenCalled()
|
|
111
103
|
expect(artificialIntelligenceController["calculateRiskProfile"]).toHaveBeenCalled()
|
|
112
|
-
expect(artificialIntelligenceController["
|
|
104
|
+
expect(artificialIntelligenceController["calculateSuspiciousMetrics"]).toHaveBeenCalled()
|
|
113
105
|
})
|
|
114
106
|
})
|
|
115
107
|
|
|
116
|
-
describe("apply
|
|
117
|
-
it("should call store.dispatch", () => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
describe("apply suspicious metric", () => {
|
|
109
|
+
it("should call store.dispatch with all needed states for desired suspicious metric view", () => {
|
|
110
|
+
storeService.dispatch(setState(STATE))
|
|
111
|
+
const testMetricSuggestionParameters = {
|
|
112
|
+
metric: "loc",
|
|
113
|
+
from: 365,
|
|
114
|
+
to: 554,
|
|
115
|
+
max: 0,
|
|
116
|
+
min: 0,
|
|
117
|
+
isOutlier: undefined
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const colorRange: ColorRange = {
|
|
121
|
+
from: testMetricSuggestionParameters.from,
|
|
122
|
+
to: testMetricSuggestionParameters.to,
|
|
123
|
+
max: 0,
|
|
124
|
+
min: 0
|
|
125
|
+
}
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks = [testMetricSuggestionParameters]
|
|
128
128
|
storeService.dispatch = jest.fn()
|
|
129
|
-
|
|
129
|
+
artificialIntelligenceController.applySuspiciousMetric(testMetricSuggestionParameters, false)
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setAreaMetric("rloc"))
|
|
132
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setHeightMetric("loc"))
|
|
133
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setColorMetric("loc"))
|
|
134
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setColorRange(colorRange))
|
|
135
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setMapColors(STATE.appSettings.mapColors))
|
|
136
|
+
})
|
|
132
137
|
|
|
133
|
-
|
|
138
|
+
it("should call store.dispatch with all needed states for desired very high risk metric view", () => {
|
|
139
|
+
storeService.dispatch(setState(STATE))
|
|
140
|
+
const mapColors = { ...STATE.appSettings.mapColors }
|
|
141
|
+
mapColors.positive = "#ffffff"
|
|
142
|
+
mapColors.neutral = "#ffffff"
|
|
143
|
+
mapColors.negative = "#A900C0"
|
|
144
|
+
|
|
145
|
+
const testMetricSuggestionParameters = {
|
|
146
|
+
metric: "loc",
|
|
147
|
+
from: 365,
|
|
148
|
+
to: 554,
|
|
149
|
+
max: 0,
|
|
150
|
+
min: 0,
|
|
151
|
+
isOutlier: true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const colorRange: ColorRange = {
|
|
155
|
+
from: testMetricSuggestionParameters.from,
|
|
156
|
+
to: testMetricSuggestionParameters.to,
|
|
157
|
+
max: 0,
|
|
158
|
+
min: 0
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks = [testMetricSuggestionParameters]
|
|
162
|
+
storeService.dispatch = jest.fn()
|
|
163
|
+
artificialIntelligenceController.applySuspiciousMetric(testMetricSuggestionParameters, true)
|
|
164
|
+
|
|
165
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setAreaMetric("rloc"))
|
|
166
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setHeightMetric("loc"))
|
|
167
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setColorMetric("loc"))
|
|
168
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setColorRange(colorRange))
|
|
169
|
+
expect(storeService.dispatch).toHaveBeenCalledWith(setMapColors(mapColors))
|
|
134
170
|
})
|
|
135
171
|
})
|
|
136
172
|
|
|
@@ -141,7 +177,7 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
141
177
|
expect(artificialIntelligenceController["getMostFrequentLanguage"]).not.toHaveBeenCalled()
|
|
142
178
|
})
|
|
143
179
|
|
|
144
|
-
it("should not calculate risk profile and
|
|
180
|
+
it("should not calculate risk profile and suspicious metrics on empty main programming language", () => {
|
|
145
181
|
artificialIntelligenceController["clearRiskProfile"] = jest.fn()
|
|
146
182
|
artificialIntelligenceController["calculateRiskProfile"] = jest.fn()
|
|
147
183
|
artificialIntelligenceController["createCustomConfigSuggestions"] = jest.fn()
|
|
@@ -155,7 +191,7 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
155
191
|
|
|
156
192
|
it("should clear and calculate risk profile for Java map", () => {
|
|
157
193
|
artificialIntelligenceController["clearRiskProfile"] = jest.fn()
|
|
158
|
-
artificialIntelligenceController["
|
|
194
|
+
artificialIntelligenceController["calculateSuspiciousMetrics"] = jest.fn()
|
|
159
195
|
|
|
160
196
|
storeService.dispatch(setExperimentalFeaturesEnabled(true))
|
|
161
197
|
artificialIntelligenceController.onFilesSelectionChanged(FILE_STATES_JAVA)
|
|
@@ -163,7 +199,7 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
163
199
|
expect(artificialIntelligenceController["_viewModel"].analyzedProgrammingLanguage).toBe("java")
|
|
164
200
|
expect(artificialIntelligenceController["clearRiskProfile"]).toHaveBeenCalled()
|
|
165
201
|
expect(artificialIntelligenceController["_viewModel"].riskProfile).toMatchSnapshot()
|
|
166
|
-
expect(artificialIntelligenceController["
|
|
202
|
+
expect(artificialIntelligenceController["calculateSuspiciousMetrics"]).toHaveBeenCalled()
|
|
167
203
|
})
|
|
168
204
|
|
|
169
205
|
it("should calculate risk profile and should not exceed 100 percent", () => {
|
|
@@ -182,7 +218,7 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
182
218
|
expect(sumOfRiskPercentage).toEqual(100)
|
|
183
219
|
})
|
|
184
220
|
|
|
185
|
-
it("should
|
|
221
|
+
it("should calculate suspicious metrics sorted by isOutlier", () => {
|
|
186
222
|
artificialIntelligenceController["clearRiskProfile"] = jest.fn()
|
|
187
223
|
artificialIntelligenceController["calculateRiskProfile"] = jest.fn()
|
|
188
224
|
|
|
@@ -193,18 +229,12 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
193
229
|
expect(artificialIntelligenceController["clearRiskProfile"]).toHaveBeenCalled()
|
|
194
230
|
expect(artificialIntelligenceController["calculateRiskProfile"]).toHaveBeenCalled()
|
|
195
231
|
|
|
196
|
-
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks[0].generalCustomConfigId = "mocked"
|
|
197
|
-
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks[0].outlierCustomConfigId = "mocked"
|
|
198
|
-
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks[1].generalCustomConfigId = "mocked"
|
|
199
|
-
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks[1].outlierCustomConfigId = "mocked"
|
|
200
|
-
artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks[2].generalCustomConfigId = "mocked"
|
|
201
|
-
|
|
202
232
|
expect(artificialIntelligenceController["_viewModel"].suspiciousMetricSuggestionLinks).toMatchSnapshot()
|
|
203
233
|
expect(artificialIntelligenceController["_viewModel"].unsuspiciousMetrics).toMatchSnapshot()
|
|
204
234
|
})
|
|
205
235
|
|
|
206
236
|
it("should calculate risk profile for not excluded files only", () => {
|
|
207
|
-
artificialIntelligenceController["
|
|
237
|
+
artificialIntelligenceController["calculateSuspiciousMetrics"] = jest.fn()
|
|
208
238
|
|
|
209
239
|
const codeMapNodeToExclude = FILE_STATES_JAVA[0].file.map.children[0].children[0]
|
|
210
240
|
|
|
@@ -220,11 +250,11 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
220
250
|
|
|
221
251
|
expect(artificialIntelligenceController["_viewModel"].analyzedProgrammingLanguage).toBe("java")
|
|
222
252
|
expect(artificialIntelligenceController["_viewModel"].riskProfile).toMatchSnapshot()
|
|
223
|
-
expect(artificialIntelligenceController["
|
|
253
|
+
expect(artificialIntelligenceController["calculateSuspiciousMetrics"]).toHaveBeenCalled()
|
|
224
254
|
})
|
|
225
255
|
|
|
226
256
|
it("should calculate risk profile but skip files with missing metrics", () => {
|
|
227
|
-
artificialIntelligenceController["
|
|
257
|
+
artificialIntelligenceController["calculateSuspiciousMetrics"] = jest.fn()
|
|
228
258
|
|
|
229
259
|
const FILE_STATES_MISSING_METRICS = klona(FILE_STATES_JAVA)
|
|
230
260
|
for (const codeMapNode of FILE_STATES_MISSING_METRICS[0].file.map.children) {
|
|
@@ -238,12 +268,12 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
238
268
|
|
|
239
269
|
expect(artificialIntelligenceController["_viewModel"].analyzedProgrammingLanguage).toBe("java")
|
|
240
270
|
expect(artificialIntelligenceController["_viewModel"].riskProfile).toBeUndefined()
|
|
241
|
-
expect(artificialIntelligenceController["
|
|
271
|
+
expect(artificialIntelligenceController["calculateSuspiciousMetrics"]).toHaveBeenCalled()
|
|
242
272
|
})
|
|
243
273
|
|
|
244
|
-
it("should calculate risk profile and
|
|
274
|
+
it("should calculate risk profile and suspicious metrics for maps with other programming languages", () => {
|
|
245
275
|
artificialIntelligenceController["calculateRiskProfile"] = jest.fn()
|
|
246
|
-
artificialIntelligenceController["
|
|
276
|
+
artificialIntelligenceController["calculateSuspiciousMetrics"] = jest.fn()
|
|
247
277
|
|
|
248
278
|
const FILE_STATES_OTHER = klona(FILE_STATES_JAVA)
|
|
249
279
|
for (const codeMapNode of FILE_STATES_OTHER[0].file.map.children) {
|
|
@@ -257,7 +287,7 @@ describe("ArtificialIntelligenceController", () => {
|
|
|
257
287
|
|
|
258
288
|
expect(artificialIntelligenceController["_viewModel"].analyzedProgrammingLanguage).toBe("other")
|
|
259
289
|
expect(artificialIntelligenceController["calculateRiskProfile"]).toHaveBeenCalled()
|
|
260
|
-
expect(artificialIntelligenceController["
|
|
290
|
+
expect(artificialIntelligenceController["calculateSuspiciousMetrics"]).toHaveBeenCalled()
|
|
261
291
|
})
|
|
262
292
|
})
|
|
263
293
|
|
|
@@ -3,19 +3,13 @@ import debounce from "lodash.debounce"
|
|
|
3
3
|
import { FilesSelectionSubscriber, FilesService } from "../../state/store/files/files.service"
|
|
4
4
|
import { FileState } from "../../model/files/files"
|
|
5
5
|
import { IRootScopeService } from "angular"
|
|
6
|
-
import { buildCustomConfigFromState } from "../../util/customConfigBuilder"
|
|
7
|
-
import { CustomConfigHelper } from "../../util/customConfigHelper"
|
|
8
6
|
import { StoreService } from "../../state/store.service"
|
|
9
|
-
import { klona } from "klona"
|
|
10
|
-
import { CustomConfig, CustomConfigMapSelectionMode } from "../../model/customConfig/customConfig.api.model"
|
|
11
7
|
import { pushSorted } from "../../util/nodeDecorator"
|
|
12
|
-
import { BlacklistItem, BlacklistType, CodeMapNode, ColorRange, NodeType
|
|
8
|
+
import { BlacklistItem, BlacklistType, CodeMapNode, ColorRange, NodeType } from "../../codeCharta.model"
|
|
13
9
|
import { hierarchy } from "d3-hierarchy"
|
|
14
10
|
import { getVisibleFileStates } from "../../model/files/files.helper"
|
|
15
11
|
import { metricThresholds } from "./artificialIntelligence.metricThresholds"
|
|
16
|
-
import { defaultMapColors } from "../../state/store/appSettings/mapColors/mapColors.actions"
|
|
17
|
-
import { ThreeOrbitControlsService } from "../codeMap/threeViewer/threeOrbitControlsService"
|
|
18
|
-
import { ThreeCameraService } from "../codeMap/threeViewer/threeCameraService"
|
|
12
|
+
import { defaultMapColors, setMapColors } from "../../state/store/appSettings/mapColors/mapColors.actions"
|
|
19
13
|
import { BlacklistService, BlacklistSubscriber } from "../../state/store/fileSettings/blacklist/blacklist.service"
|
|
20
14
|
import { isPathBlacklisted } from "../../util/codeMapHelper"
|
|
21
15
|
import {
|
|
@@ -24,6 +18,10 @@ import {
|
|
|
24
18
|
} from "../../state/store/appSettings/enableExperimentalFeatures/experimentalFeaturesEnabled.service"
|
|
25
19
|
import { metricDescriptions } from "../../util/metric/metricDescriptions"
|
|
26
20
|
import percentRound from "percent-round"
|
|
21
|
+
import { setColorRange } from "../../state/store/dynamicSettings/colorRange/colorRange.actions"
|
|
22
|
+
import { setHeightMetric } from "../../state/store/dynamicSettings/heightMetric/heightMetric.actions"
|
|
23
|
+
import { setColorMetric } from "../../state/store/dynamicSettings/colorMetric/colorMetric.actions"
|
|
24
|
+
import { setAreaMetric } from "../../state/store/dynamicSettings/areaMetric/areaMetric.actions"
|
|
27
25
|
|
|
28
26
|
interface MetricValues {
|
|
29
27
|
[metric: string]: number[]
|
|
@@ -39,8 +37,7 @@ interface MetricSuggestionParameters {
|
|
|
39
37
|
metric: string
|
|
40
38
|
from: number
|
|
41
39
|
to: number
|
|
42
|
-
|
|
43
|
-
outlierCustomConfigId?: string
|
|
40
|
+
isOutlier?: boolean
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
export class ArtificialIntelligenceController
|
|
@@ -56,7 +53,6 @@ export class ArtificialIntelligenceController
|
|
|
56
53
|
highRisk: number
|
|
57
54
|
veryHighRisk: number
|
|
58
55
|
}
|
|
59
|
-
isHighRiskFilesModeEnabled: boolean
|
|
60
56
|
} = {
|
|
61
57
|
analyzedProgrammingLanguage: undefined,
|
|
62
58
|
suspiciousMetricSuggestionLinks: [],
|
|
@@ -66,20 +62,14 @@ export class ArtificialIntelligenceController
|
|
|
66
62
|
moderateRisk: 0,
|
|
67
63
|
highRisk: 0,
|
|
68
64
|
veryHighRisk: 0
|
|
69
|
-
}
|
|
70
|
-
isHighRiskFilesModeEnabled: false
|
|
65
|
+
}
|
|
71
66
|
}
|
|
72
67
|
|
|
73
68
|
private debounceCalculation: () => void
|
|
74
69
|
private fileState: FileState
|
|
75
70
|
private blacklist: BlacklistItem[] = []
|
|
76
71
|
|
|
77
|
-
constructor(
|
|
78
|
-
private $rootScope: IRootScopeService,
|
|
79
|
-
private storeService: StoreService,
|
|
80
|
-
private threeOrbitControlsService: ThreeOrbitControlsService,
|
|
81
|
-
private threeCameraService: ThreeCameraService
|
|
82
|
-
) {
|
|
72
|
+
constructor(private $rootScope: IRootScopeService, private storeService: StoreService) {
|
|
83
73
|
"ngInject"
|
|
84
74
|
FilesService.subscribe(this.$rootScope, this)
|
|
85
75
|
BlacklistService.subscribe(this.$rootScope, this)
|
|
@@ -96,8 +86,32 @@ export class ArtificialIntelligenceController
|
|
|
96
86
|
}
|
|
97
87
|
}
|
|
98
88
|
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
applySuspiciousMetric(metric: MetricSuggestionParameters, isOutlier: boolean) {
|
|
90
|
+
if (this._viewModel.suspiciousMetricSuggestionLinks.includes(metric)) {
|
|
91
|
+
const mapColors = { ...this.storeService.getState().appSettings.mapColors }
|
|
92
|
+
const colorRange: ColorRange = {
|
|
93
|
+
from: metric.from,
|
|
94
|
+
to: metric.to,
|
|
95
|
+
max: 0,
|
|
96
|
+
min: 0
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (metric.isOutlier === isOutlier) {
|
|
100
|
+
mapColors.positive = "#ffffff"
|
|
101
|
+
mapColors.neutral = "#ffffff"
|
|
102
|
+
mapColors.negative = "#A900C0"
|
|
103
|
+
} else {
|
|
104
|
+
mapColors.positive = defaultMapColors.positive
|
|
105
|
+
mapColors.neutral = defaultMapColors.neutral
|
|
106
|
+
mapColors.negative = defaultMapColors.negative
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.storeService.dispatch(setAreaMetric("rloc"))
|
|
110
|
+
this.storeService.dispatch(setHeightMetric(metric.metric))
|
|
111
|
+
this.storeService.dispatch(setColorMetric(metric.metric))
|
|
112
|
+
this.storeService.dispatch(setColorRange(colorRange))
|
|
113
|
+
this.storeService.dispatch(setMapColors(mapColors))
|
|
114
|
+
}
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
onBlacklistChanged(blacklist: BlacklistItem[]) {
|
|
@@ -132,7 +146,7 @@ export class ArtificialIntelligenceController
|
|
|
132
146
|
|
|
133
147
|
if (mainProgrammingLanguage !== undefined) {
|
|
134
148
|
this.calculateRiskProfile(this.fileState, mainProgrammingLanguage, "mcc")
|
|
135
|
-
this.
|
|
149
|
+
this.calculateSuspiciousMetrics(this.fileState, mainProgrammingLanguage)
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
|
|
@@ -198,43 +212,23 @@ export class ArtificialIntelligenceController
|
|
|
198
212
|
}
|
|
199
213
|
}
|
|
200
214
|
|
|
201
|
-
private
|
|
215
|
+
private calculateSuspiciousMetrics(fileState: FileState, programmingLanguage) {
|
|
202
216
|
const metricValues = this.getSortedMetricValues(fileState, programmingLanguage)
|
|
203
217
|
const metricAssessmentResults = this.findGoodAndBadMetrics(metricValues, programmingLanguage)
|
|
204
|
-
|
|
205
218
|
const noticeableMetricSuggestionLinks = new Map<string, MetricSuggestionParameters>()
|
|
206
|
-
const newCustomConfigs: CustomConfig[] = []
|
|
207
219
|
|
|
208
220
|
for (const [metricName, colorRange] of metricAssessmentResults.suspiciousMetrics) {
|
|
209
|
-
const suspiciousMetricConfig = this.createOrUpdateCustomConfig(
|
|
210
|
-
`Suspicious Files - Metric ${metricName.toUpperCase()} - (AI)`,
|
|
211
|
-
this.prepareOverviewConfigState(metricName, colorRange.from, colorRange.to),
|
|
212
|
-
fileState
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
newCustomConfigs.push(suspiciousMetricConfig)
|
|
216
|
-
|
|
217
221
|
noticeableMetricSuggestionLinks.set(metricName, {
|
|
218
222
|
metric: metricName,
|
|
219
|
-
...colorRange
|
|
220
|
-
generalCustomConfigId: suspiciousMetricConfig.id
|
|
223
|
+
...colorRange
|
|
221
224
|
})
|
|
222
225
|
|
|
223
226
|
const outlierThreshold = metricAssessmentResults.outliersThresholds.get(metricName)
|
|
224
227
|
if (outlierThreshold > 0) {
|
|
225
|
-
|
|
226
|
-
`Very High Risk Files - Metric ${metricName.toUpperCase()} - (AI)`,
|
|
227
|
-
this.prepareOutlierConfigState(metricName, outlierThreshold - 1, outlierThreshold),
|
|
228
|
-
fileState
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
newCustomConfigs.push(highRiskMetricConfig)
|
|
232
|
-
|
|
233
|
-
noticeableMetricSuggestionLinks.get(metricName).outlierCustomConfigId = highRiskMetricConfig.id
|
|
228
|
+
noticeableMetricSuggestionLinks.get(metricName).isOutlier = true
|
|
234
229
|
}
|
|
235
230
|
}
|
|
236
231
|
|
|
237
|
-
CustomConfigHelper.addCustomConfigs(newCustomConfigs)
|
|
238
232
|
this._viewModel.suspiciousMetricSuggestionLinks = [...noticeableMetricSuggestionLinks.values()].sort(
|
|
239
233
|
this.compareSuspiciousMetricSuggestionLinks
|
|
240
234
|
)
|
|
@@ -242,8 +236,8 @@ export class ArtificialIntelligenceController
|
|
|
242
236
|
}
|
|
243
237
|
|
|
244
238
|
private compareSuspiciousMetricSuggestionLinks(a: MetricSuggestionParameters, b: MetricSuggestionParameters): number {
|
|
245
|
-
if (a.
|
|
246
|
-
if (!a.
|
|
239
|
+
if (a.isOutlier && !b.isOutlier) return -1
|
|
240
|
+
if (!a.isOutlier && b.isOutlier) return 1
|
|
247
241
|
return 0
|
|
248
242
|
}
|
|
249
243
|
|
|
@@ -284,60 +278,6 @@ export class ArtificialIntelligenceController
|
|
|
284
278
|
return metricAssessmentResults
|
|
285
279
|
}
|
|
286
280
|
|
|
287
|
-
private createOrUpdateCustomConfig(configName: string, state: State, fileState: FileState) {
|
|
288
|
-
const customConfig = CustomConfigHelper.getCustomConfigByName(
|
|
289
|
-
CustomConfigMapSelectionMode.SINGLE,
|
|
290
|
-
[fileState.file.fileMeta.fileName],
|
|
291
|
-
configName
|
|
292
|
-
)
|
|
293
|
-
|
|
294
|
-
// If it exists, create a fresh one with current thresholds
|
|
295
|
-
if (customConfig !== null) {
|
|
296
|
-
CustomConfigHelper.deleteCustomConfig(customConfig.id)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return buildCustomConfigFromState(configName, state)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
private prepareOverviewConfigState(metricName: string, colorRangeFrom: number, colorRangeTo: number) {
|
|
303
|
-
const state = klona(this.storeService.getState())
|
|
304
|
-
|
|
305
|
-
// Just use rloc as area metric
|
|
306
|
-
state.dynamicSettings.areaMetric = "rloc"
|
|
307
|
-
|
|
308
|
-
// Use bad metric as height and color metric
|
|
309
|
-
state.dynamicSettings.heightMetric = metricName
|
|
310
|
-
state.dynamicSettings.colorMetric = metricName
|
|
311
|
-
state.dynamicSettings.colorRange.from = colorRangeFrom
|
|
312
|
-
state.dynamicSettings.colorRange.to = colorRangeTo
|
|
313
|
-
|
|
314
|
-
state.appSettings.mapColors.positive = defaultMapColors.positive
|
|
315
|
-
state.appSettings.mapColors.neutral = defaultMapColors.neutral
|
|
316
|
-
state.appSettings.mapColors.negative = defaultMapColors.negative
|
|
317
|
-
|
|
318
|
-
return state
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
private prepareOutlierConfigState(metricName: string, colorRangeFrom: number, colorRangeTo: number) {
|
|
322
|
-
const state = klona(this.storeService.getState())
|
|
323
|
-
|
|
324
|
-
// just use rloc as area metric
|
|
325
|
-
state.dynamicSettings.areaMetric = "rloc"
|
|
326
|
-
|
|
327
|
-
// use bad metric as height and color metric
|
|
328
|
-
state.dynamicSettings.heightMetric = metricName
|
|
329
|
-
state.dynamicSettings.colorMetric = metricName
|
|
330
|
-
|
|
331
|
-
state.dynamicSettings.colorRange.to = colorRangeTo
|
|
332
|
-
state.dynamicSettings.colorRange.from = colorRangeFrom
|
|
333
|
-
|
|
334
|
-
state.appSettings.mapColors.positive = "#ffffff"
|
|
335
|
-
state.appSettings.mapColors.neutral = "#ffffff"
|
|
336
|
-
state.appSettings.mapColors.negative = "#A900C0"
|
|
337
|
-
|
|
338
|
-
return state
|
|
339
|
-
}
|
|
340
|
-
|
|
341
281
|
private getSortedMetricValues(fileState: FileState, programmingLanguage): MetricValues {
|
|
342
282
|
const metricValues: MetricValues = {}
|
|
343
283
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EdgeVisibility } from "../../codeCharta.model"
|
|
2
2
|
import { StoreService } from "../../state/store.service"
|
|
3
3
|
import { setEdges } from "../../state/store/fileSettings/edges/edges.actions"
|
|
4
|
-
import { unmarkPackage, setMarkedPackages } from "../../state/store/fileSettings/markedPackages/markedPackages.actions"
|
|
5
|
-
import { addMarkedPackage } from "../../state/store/fileSettings/markedPackages/util/addMarkedPackage"
|
|
6
4
|
import { EdgeMetricDataService } from "../../state/store/metricData/edgeMetricData/edgeMetricData.service"
|
|
7
5
|
|
|
8
6
|
export class CodeMapActionsService {
|
|
@@ -10,26 +8,6 @@ export class CodeMapActionsService {
|
|
|
10
8
|
"ngInject"
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
markFolder({ path }: { path?: string }, color: string) {
|
|
14
|
-
const { markedPackages } = this.storeService.getState().fileSettings
|
|
15
|
-
const markedPackagesMap = new Map(markedPackages.map(entry => [entry.path, entry]))
|
|
16
|
-
addMarkedPackage(markedPackagesMap, { path, color })
|
|
17
|
-
|
|
18
|
-
this.storeService.dispatch(setMarkedPackages([...markedPackagesMap.values()]))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
unmarkFolder(node: CodeMapNode) {
|
|
22
|
-
let index = this.storeService.getState().fileSettings.markedPackages.findIndex(mp => mp.path === node.path)
|
|
23
|
-
|
|
24
|
-
if (index === -1) {
|
|
25
|
-
index = this.getParentMarkedPackageIndex(node.path)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (index !== -1) {
|
|
29
|
-
this.storeService.dispatch(unmarkPackage(index))
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
11
|
updateEdgePreviews() {
|
|
34
12
|
const state = this.storeService.getState()
|
|
35
13
|
const { edges } = state.fileSettings
|
|
@@ -58,21 +36,4 @@ export class CodeMapActionsService {
|
|
|
58
36
|
|
|
59
37
|
this.storeService.dispatch(setEdges(edges))
|
|
60
38
|
}
|
|
61
|
-
|
|
62
|
-
getParentMarkedPackageIndex(path: string) {
|
|
63
|
-
const markedPackages: MarkedPackage[] = this.storeService.getState().fileSettings.markedPackages
|
|
64
|
-
let index = -1
|
|
65
|
-
for (let loopIndex = 0; loopIndex < markedPackages.length; loopIndex++) {
|
|
66
|
-
const markedPackage = markedPackages[loopIndex]
|
|
67
|
-
if (
|
|
68
|
-
path.startsWith(markedPackage.path) &&
|
|
69
|
-
path !== markedPackage.path &&
|
|
70
|
-
(index === -1 || markedPackages[index].path.length < markedPackage.path.length)
|
|
71
|
-
) {
|
|
72
|
-
index = loopIndex
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return index
|
|
77
|
-
}
|
|
78
39
|
}
|
|
@@ -18,6 +18,7 @@ import { CodeMapPreRenderService } from "./codeMap.preRender.service"
|
|
|
18
18
|
import { ThreeViewerService } from "./threeViewer/threeViewerService"
|
|
19
19
|
import { setHoveredBuildingPath } from "../../state/store/appStatus/hoveredBuildingPath/hoveredBuildingPath.actions"
|
|
20
20
|
import { hoveredBuildingPathSelector } from "../../state/store/appStatus/hoveredBuildingPath/hoveredBuildingPath.selector"
|
|
21
|
+
import type { BUILDING_RIGHT_CLICKED_EVENT_TYPE } from "../../../../src/globals"
|
|
21
22
|
|
|
22
23
|
interface Coordinates {
|
|
23
24
|
x: number
|
|
@@ -32,10 +33,6 @@ export interface BuildingUnhoveredSubscriber {
|
|
|
32
33
|
onBuildingUnhovered()
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
export interface BuildingRightClickedEventSubscriber {
|
|
36
|
-
onBuildingRightClicked(building: CodeMapBuilding, x: number, y: number)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
36
|
export enum ClickType {
|
|
40
37
|
LeftClick = 0,
|
|
41
38
|
RightClick = 2
|
|
@@ -51,7 +48,6 @@ export enum CursorType {
|
|
|
51
48
|
export class CodeMapMouseEventService implements ViewCubeEventPropagationSubscriber, FilesSelectionSubscriber, BlacklistSubscriber {
|
|
52
49
|
private static readonly BUILDING_HOVERED_EVENT = "building-hovered"
|
|
53
50
|
private static readonly BUILDING_UNHOVERED_EVENT = "building-unhovered"
|
|
54
|
-
private static readonly BUILDING_RIGHT_CLICKED_EVENT = "building-right-clicked"
|
|
55
51
|
private readonly THRESHOLD_FOR_MOUSE_MOVEMENT_TRACKING = 3
|
|
56
52
|
|
|
57
53
|
private highlightedInTreeView: CodeMapBuilding
|
|
@@ -115,12 +111,6 @@ export class CodeMapMouseEventService implements ViewCubeEventPropagationSubscri
|
|
|
115
111
|
})
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
static subscribeToBuildingRightClickedEvents($rootScope: IRootScopeService, subscriber: BuildingRightClickedEventSubscriber) {
|
|
119
|
-
$rootScope.$on(this.BUILDING_RIGHT_CLICKED_EVENT, (_event, data) => {
|
|
120
|
-
subscriber.onBuildingRightClicked(data.building, data.x, data.y)
|
|
121
|
-
})
|
|
122
|
-
}
|
|
123
|
-
|
|
124
114
|
start() {
|
|
125
115
|
// TODO: Check if these event listeners should ever be removed again.
|
|
126
116
|
this.threeRendererService.renderer.domElement.addEventListener("mousemove", event => this.onDocumentMouseMove(event))
|
|
@@ -365,11 +355,15 @@ export class CodeMapMouseEventService implements ViewCubeEventPropagationSubscri
|
|
|
365
355
|
// Check if mouse moved to prevent the node context menu to show up
|
|
366
356
|
// after moving the map, when the cursor ends on a building.
|
|
367
357
|
if (building && !this.hasMouseMovedMoreThanThreePixels(this.mouseOnLastClick)) {
|
|
368
|
-
|
|
369
|
-
building,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
358
|
+
document.dispatchEvent(
|
|
359
|
+
new CustomEvent<BUILDING_RIGHT_CLICKED_EVENT_TYPE>("building-right-clicked", {
|
|
360
|
+
detail: {
|
|
361
|
+
building,
|
|
362
|
+
x: this.mouse.x,
|
|
363
|
+
y: this.mouse.y
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
)
|
|
373
367
|
}
|
|
374
368
|
this.threeUpdateCycleService.update()
|
|
375
369
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
></
|
|
1
|
+
<ng-container
|
|
2
|
+
[ngTemplateOutlet]="triggerTemplate"
|
|
3
|
+
[ngTemplateOutletContext]="{ isOpen: colorPickerMenuTrigger.menuOpen, isHovered: isHovered }"
|
|
4
|
+
>
|
|
5
|
+
</ng-container>
|
|
6
|
+
|
|
7
|
+
<span [matMenuTriggerFor]="menu" #colorPickerMenuTrigger="matMenuTrigger"></span>
|
|
8
|
+
<mat-menu #menu="matMenu" class="cc-color-picker-menu" [xPosition]="openXPosition" [hasBackdrop]="false">
|
|
9
|
+
<color-chrome
|
|
10
|
+
role="colorpicker"
|
|
11
|
+
(click)="$event.stopPropagation()"
|
|
12
|
+
[disableAlpha]="true"
|
|
13
|
+
[color]="hexColor"
|
|
14
|
+
(onChangeComplete)="handleChangeComplete($event.color.hex)"
|
|
15
|
+
></color-chrome>
|
|
16
|
+
</mat-menu>
|