1
+ /*global document window*/
1
2
import type GraphContainer from '@gitkraken/gitkraken-components' ;
2
- import type { GraphRow , GraphZoneType } from '@gitkraken/gitkraken-components' ;
3
+ import type { CssVariables , GraphRow , GraphZoneType } from '@gitkraken/gitkraken-components' ;
3
4
import { consume } from '@lit/context' ;
4
5
import { SignalWatcher } from '@lit-labs/signals' ;
5
6
import { html , LitElement } from 'lit' ;
6
- import { customElement , query } from 'lit/decorators.js' ;
7
+ import { customElement , query , state } from 'lit/decorators.js' ;
7
8
import { ifDefined } from 'lit/directives/if-defined.js' ;
8
9
import type { GitGraphRowType } from '../../../../../git/models/graph' ;
9
10
import { filterMap } from '../../../../../system/array' ;
11
+ import { getCssMixedColorValue , getCssOpacityColorValue , getCssVariable } from '../../../../../system/color' ;
12
+ import { debounce } from '../../../../../system/function/debounce' ;
10
13
import {
11
14
DoubleClickedCommandType ,
12
15
GetMissingAvatarsCommand ,
@@ -20,10 +23,28 @@ import type { CustomEventType } from '../../../shared/components/element';
20
23
import { ipcContext } from '../../../shared/contexts/ipc' ;
21
24
import type { TelemetryContext } from '../../../shared/contexts/telemetry' ;
22
25
import { telemetryContext } from '../../../shared/contexts/telemetry' ;
26
+ import type { Disposable } from '../../../shared/events' ;
27
+ import type { ThemeChangeEvent } from '../../../shared/theme' ;
28
+ import { onDidChangeTheme } from '../../../shared/theme' ;
23
29
import { stateContext } from '../context' ;
24
30
import { graphStateContext } from '../stateProvider' ;
25
- import type { GlGraph } from './graph-wrapper-element' ;
26
- import './graph-wrapper-element' ;
31
+ import type { GlGraph } from './gl-graph' ;
32
+ import type { GraphWrapperTheming } from './gl-graph.react' ;
33
+ import './gl-graph' ;
34
+
35
+ // These properties in the DOM are auto-generated by VS Code from our `contributes.colors` in package.json
36
+ const graphLaneThemeColors = new Map ( [
37
+ [ '--vscode-gitlens-graphLane1Color' , '#15a0bf' ] ,
38
+ [ '--vscode-gitlens-graphLane2Color' , '#0669f7' ] ,
39
+ [ '--vscode-gitlens-graphLane3Color' , '#8e00c2' ] ,
40
+ [ '--vscode-gitlens-graphLane4Color' , '#c517b6' ] ,
41
+ [ '--vscode-gitlens-graphLane5Color' , '#d90171' ] ,
42
+ [ '--vscode-gitlens-graphLane6Color' , '#cd0101' ] ,
43
+ [ '--vscode-gitlens-graphLane7Color' , '#f25d2e' ] ,
44
+ [ '--vscode-gitlens-graphLane8Color' , '#f2ca33' ] ,
45
+ [ '--vscode-gitlens-graphLane9Color' , '#7bd938' ] ,
46
+ [ '--vscode-gitlens-graphLane10Color' , '#2ece9d' ] ,
47
+ ] ) ;
27
48
28
49
declare global {
29
50
// interface HTMLElementTagNameMap {
@@ -57,6 +78,8 @@ export class GlGraphWrapper extends SignalWatcher(LitElement) {
57
78
return this ;
58
79
}
59
80
81
+ private disposables : Disposable [ ] = [ ] ;
82
+
60
83
@consume ( { context : graphStateContext } )
61
84
private readonly graphAppState ! : typeof graphStateContext . __context__ ;
62
85
@@ -77,6 +100,29 @@ export class GlGraphWrapper extends SignalWatcher(LitElement) {
77
100
this . ref = ref ;
78
101
} ;
79
102
103
+ @state ( )
104
+ private theming ?: GraphWrapperTheming ;
105
+
106
+ override connectedCallback ( ) : void {
107
+ super . connectedCallback ?.( ) ;
108
+
109
+ this . theming = this . getGraphTheming ( ) ;
110
+ this . disposables . push (
111
+ onDidChangeTheme (
112
+ debounce ( ( e : ThemeChangeEvent ) => {
113
+ this . theming = this . getGraphTheming ( e ) ;
114
+ } , 100 ) ,
115
+ ) ,
116
+ ) ;
117
+ }
118
+
119
+ override disconnectedCallback ( ) : void {
120
+ super . disconnectedCallback ?.( ) ;
121
+
122
+ this . disposables . forEach ( d => d . dispose ( ) ) ;
123
+ this . disposables = [ ] ;
124
+ }
125
+
80
126
override render ( ) {
81
127
const { graphAppState, hostState } = this ;
82
128
@@ -100,7 +146,7 @@ export class GlGraphWrapper extends SignalWatcher(LitElement) {
100
146
.rowsStats=${ hostState . rowsStats }
101
147
.searchResults=${ graphAppState . searchResults }
102
148
.selectedRows=${ graphAppState . selectedRows }
103
- .theming=${ graphAppState . theming }
149
+ .theming=${ this . theming }
104
150
?windowFocused=${ hostState . windowFocused }
105
151
.workingTreeStats=${ hostState . workingTreeStats }
106
152
@changecolumns=${ this . onColumnsChanged }
@@ -193,4 +239,126 @@ export class GlGraphWrapper extends SignalWatcher(LitElement) {
193
239
private onVisibleDaysChanged ( { detail } : CustomEventType < 'graph-changevisibledays' > ) {
194
240
this . dispatchEvent ( new CustomEvent ( 'gl-graph-change-visible-days' , { detail : detail } ) ) ;
195
241
}
242
+
243
+ private readonly themingDefaults : { cssVariables : CssVariables ; themeOpacityFactor : number } = {
244
+ cssVariables : ( ( ) => {
245
+ const bgColor = getCssVariableValue ( '--color-background' ) ;
246
+ const mixedGraphColors : CssVariables = { } ;
247
+ let i = 0 ;
248
+ let color ;
249
+ for ( const [ colorVar , colorDefault ] of graphLaneThemeColors ) {
250
+ color = getCssVariableValue ( colorVar , { fallbackValue : colorDefault } ) ;
251
+ mixedGraphColors [ `--graph-color-${ i } ` ] = color ;
252
+ for ( const mixInt of [ 15 , 25 , 45 , 50 ] ) {
253
+ mixedGraphColors [ `--graph-color-${ i } -bg${ mixInt } ` ] = getCssMixedColorValue ( bgColor , color , mixInt ) ;
254
+ }
255
+ for ( const mixInt of [ 10 , 50 ] ) {
256
+ mixedGraphColors [ `--graph-color-${ i } -f${ mixInt } ` ] = getCssOpacityColorValue ( color , mixInt ) ;
257
+ }
258
+ i ++ ;
259
+ }
260
+ return {
261
+ '--app__bg0' : getCssVariableValue ( '--color-background' ) ,
262
+ '--panel__bg0' : getCssVariableValue ( '--color-graph-background' ) ,
263
+ '--panel__bg1' : getCssVariableValue ( '--color-graph-background2' ) ,
264
+ '--section-border' : getCssVariableValue ( '--color-graph-background2' ) ,
265
+ '--selected-row' : getCssVariableValue ( '--color-graph-selected-row' ) ,
266
+ '--selected-row-border' : 'none' ,
267
+ '--hover-row' : getCssVariableValue ( '--color-graph-hover-row' ) ,
268
+ '--hover-row-border' : 'none' ,
269
+ '--scrollable-scrollbar-thickness' : getCssVariableValue ( '--graph-column-scrollbar-thickness' ) ,
270
+ '--scroll-thumb-bg' : getCssVariableValue ( '--vscode-scrollbarSlider-background' ) ,
271
+ '--scroll-marker-head-color' : getCssVariableValue ( '--color-graph-scroll-marker-head' ) ,
272
+ '--scroll-marker-upstream-color' : getCssVariableValue ( '--color-graph-scroll-marker-upstream' ) ,
273
+ '--scroll-marker-highlights-color' : getCssVariableValue ( '--color-graph-scroll-marker-highlights' ) ,
274
+ '--scroll-marker-local-branches-color' : getCssVariableValue (
275
+ '--color-graph-scroll-marker-local-branches' ,
276
+ ) ,
277
+ '--scroll-marker-remote-branches-color' : getCssVariableValue (
278
+ '--color-graph-scroll-marker-remote-branches' ,
279
+ ) ,
280
+ '--scroll-marker-stashes-color' : getCssVariableValue ( '--color-graph-scroll-marker-stashes' ) ,
281
+ '--scroll-marker-tags-color' : getCssVariableValue ( '--color-graph-scroll-marker-tags' ) ,
282
+ '--scroll-marker-selection-color' : getCssVariableValue ( '--color-graph-scroll-marker-selection' ) ,
283
+ '--scroll-marker-pull-requests-color' : getCssVariableValue ( '--color-graph-scroll-marker-pull-requests' ) ,
284
+ '--stats-added-color' : getCssVariableValue ( '--color-graph-stats-added' ) ,
285
+ '--stats-deleted-color' : getCssVariableValue ( '--color-graph-stats-deleted' ) ,
286
+ '--stats-files-color' : getCssVariableValue ( '--color-graph-stats-files' ) ,
287
+ '--stats-bar-border-radius' : getCssVariableValue ( '--graph-stats-bar-border-radius' ) ,
288
+ '--stats-bar-height' : getCssVariableValue ( '--graph-stats-bar-height' ) ,
289
+ '--text-selected' : getCssVariableValue ( '--color-graph-text-selected' ) ,
290
+ '--text-selected-row' : getCssVariableValue ( '--color-graph-text-selected-row' ) ,
291
+ '--text-hovered' : getCssVariableValue ( '--color-graph-text-hovered' ) ,
292
+ '--text-dimmed-selected' : getCssVariableValue ( '--color-graph-text-dimmed-selected' ) ,
293
+ '--text-dimmed' : getCssVariableValue ( '--color-graph-text-dimmed' ) ,
294
+ '--text-normal' : getCssVariableValue ( '--color-graph-text-normal' ) ,
295
+ '--text-secondary' : getCssVariableValue ( '--color-graph-text-secondary' ) ,
296
+ '--text-disabled' : getCssVariableValue ( '--color-graph-text-disabled' ) ,
297
+ '--text-accent' : getCssVariableValue ( '--color-link-foreground' ) ,
298
+ '--text-inverse' : getCssVariableValue ( '--vscode-input-background' ) ,
299
+ '--text-bright' : getCssVariableValue ( '--vscode-input-background' ) ,
300
+ ...mixedGraphColors ,
301
+ } ;
302
+ } ) ( ) ,
303
+ themeOpacityFactor : 1 ,
304
+ } ;
305
+
306
+ private getGraphTheming ( e ?: ThemeChangeEvent ) : GraphWrapperTheming {
307
+ // this will be called on theme updated as well as on config updated since it is dependent on the column colors from config changes and the background color from the theme
308
+ const computedStyle = e ?. computedStyle ?? window . getComputedStyle ( document . documentElement ) ;
309
+ const bgColor = getCssVariableValue ( '--color-background' , { computedStyle : computedStyle } ) ;
310
+
311
+ const mixedGraphColors : CssVariables = { } ;
312
+
313
+ let i = 0 ;
314
+ let color ;
315
+ for ( const [ colorVar , colorDefault ] of graphLaneThemeColors ) {
316
+ color = getCssVariableValue ( colorVar , { computedStyle : computedStyle , fallbackValue : colorDefault } ) ;
317
+
318
+ mixedGraphColors [ `--column-${ i } -color` ] = getCssVariable ( colorVar , computedStyle ) || colorDefault ;
319
+
320
+ for ( const mixInt of [ 15 , 25 , 45 , 50 ] ) {
321
+ mixedGraphColors [ `--graph-color-${ i } -bg${ mixInt } ` ] = getCssMixedColorValue ( bgColor , color , mixInt ) ;
322
+ }
323
+
324
+ i ++ ;
325
+ }
326
+
327
+ const isHighContrastTheme =
328
+ e ?. isHighContrastTheme ??
329
+ ( document . body . classList . contains ( 'vscode-high-contrast' ) ||
330
+ document . body . classList . contains ( 'vscode-high-contrast-light' ) ) ;
331
+
332
+ return {
333
+ cssVariables : {
334
+ ...this . themingDefaults . cssVariables ,
335
+ '--selected-row-border' : isHighContrastTheme
336
+ ? `1px solid ${ getCssVariableValue ( '--color-graph-contrast-border' , { computedStyle : computedStyle } ) } `
337
+ : 'none' ,
338
+ '--hover-row-border' : isHighContrastTheme
339
+ ? `1px dashed ${ getCssVariableValue ( '--color-graph-contrast-border' , { computedStyle : computedStyle } ) } `
340
+ : 'none' ,
341
+ ...mixedGraphColors ,
342
+ } ,
343
+ themeOpacityFactor :
344
+ parseInt ( getCssVariable ( '--graph-theme-opacity-factor' , computedStyle ) ) ||
345
+ this . themingDefaults . themeOpacityFactor ,
346
+ } ;
347
+ }
348
+ }
349
+
350
+ function getCssVariableValue (
351
+ variable : string ,
352
+ options ?: { computedStyle ?: CSSStyleDeclaration ; fallbackValue ?: string } ,
353
+ ) : string {
354
+ const fallbackValue = options ?. computedStyle
355
+ ? getCssVariable ( variable , options ?. computedStyle )
356
+ : options ?. fallbackValue
357
+ ? options . fallbackValue
358
+ : undefined ;
359
+
360
+ if ( fallbackValue ) {
361
+ return `var(${ variable } , ${ fallbackValue } )` ;
362
+ }
363
+ return `var(${ variable } )` ;
196
364
}
0 commit comments