import { darken, lighten, adjust, invert, isDark } from 'khroma'; import { mkBorder } from './theme-helpers.js'; import { oldAttributeBackgroundColorEven, oldAttributeBackgroundColorOdd, } from './erDiagram-oldHardcodedValues.js'; class Theme { constructor() { /** # Base variables */ /** * - Background - used to know what the background color is of the diagram. This is used for * deducing colors for instance line color. Default value is #f4f4f4. */ this.background = '#ffffff'; this.primaryColor = '#cccccc'; this.mainBkg = '#ffffff'; this.noteBkgColor = '#fff5ad'; this.noteTextColor = '#28253D'; this.THEME_COLOR_LIMIT = 12; this.radius = 3; this.strokeWidth = 2; this.primaryBorderColor = mkBorder(this.primaryColor, this.darkMode); // dark this.fontFamily = 'arial, sans-serif'; this.fontSize = '14px'; // Neo-specific this.nodeBorder = '#28253D'; this.stateBorder = '#28253D'; this.useGradient = false; this.gradientStart = '#0042eb'; this.gradientStop = '#eb0042'; this.dropShadow = 'url(#drop-shadow)'; this.tertiaryColor = '#ffffff'; /* Architecture Diagram variables */ this.archEdgeColor = 'calculated'; this.archEdgeArrowColor = 'calculated'; this.archEdgeWidth = '3'; this.archGroupBorderColor = this.primaryBorderColor; this.archGroupBorderWidth = '2px'; } updateColors() { // The || is to make sure that if the variable has been defined by a user override that value is to be used /* Main */ this.primaryTextColor = this.primaryTextColor || (this.darkMode ? '#eee' : '#28253D'); // invert(this.primaryColor); this.secondaryColor = this.secondaryColor || adjust(this.primaryColor, { h: -120 }); this.tertiaryColor = this.tertiaryColor || adjust(this.primaryColor, { h: 180, l: 5 }); this.primaryBorderColor = this.primaryBorderColor || mkBorder(this.primaryColor, this.darkMode); this.secondaryBorderColor = this.secondaryBorderColor || mkBorder(this.secondaryColor, this.darkMode); this.tertiaryBorderColor = this.tertiaryBorderColor || mkBorder(this.tertiaryColor, this.darkMode); this.noteBorderColor = this.noteBorderColor || mkBorder(this.noteBkgColor, this.darkMode); this.noteBkgColor = this.noteBkgColor || '#fff5ad'; this.noteTextColor = this.noteTextColor || '#28253D'; this.secondaryTextColor = this.secondaryTextColor || invert(this.secondaryColor); this.tertiaryTextColor = this.tertiaryTextColor || invert(this.tertiaryColor); this.lineColor = this.lineColor || invert(this.background); this.arrowheadColor = this.arrowheadColor || invert(this.background); this.textColor = this.textColor || this.primaryTextColor; // TODO: should this instead default to secondaryBorderColor? this.border2 = this.border2 || this.tertiaryBorderColor; /* Flowchart variables */ this.nodeBkg = this.nodeBkg || this.primaryColor; this.mainBkg = this.mainBkg || this.primaryColor; this.nodeBorder = this.nodeBorder || this.primaryBorderColor; this.clusterBkg = this.clusterBkg || this.tertiaryColor; this.clusterBorder = this.clusterBorder || this.tertiaryBorderColor; this.defaultLinkColor = this.defaultLinkColor || this.lineColor; this.titleColor = this.titleColor || this.tertiaryTextColor; this.edgeLabelBackground = this.edgeLabelBackground || (this.darkMode ? darken(this.secondaryColor, 30) : this.secondaryColor); this.nodeTextColor = this.nodeTextColor || this.primaryTextColor; /* Sequence Diagram variables */ // this.actorBorder = lighten(this.border1, 0.5); this.actorBorder = this.actorBorder || this.primaryBorderColor; this.actorBkg = this.actorBkg || this.mainBkg; this.actorTextColor = this.actorTextColor || this.primaryTextColor; this.actorLineColor = this.actorLineColor || this.actorBorder; this.labelBoxBkgColor = this.labelBoxBkgColor || this.actorBkg; this.signalColor = this.signalColor || this.textColor; this.signalTextColor = this.signalTextColor || this.textColor; this.labelBoxBorderColor = this.labelBoxBorderColor || this.actorBorder; this.labelTextColor = this.labelTextColor || this.actorTextColor; this.loopTextColor = this.loopTextColor || this.actorTextColor; this.activationBorderColor = this.activationBorderColor || darken(this.secondaryColor, 10); this.activationBkgColor = this.activationBkgColor || this.secondaryColor; this.sequenceNumberColor = this.sequenceNumberColor || invert(this.lineColor); /* Gantt chart variables */ const primaryColor = '#ECECFE'; const secondaryColor = '#E9E9F1'; const tertiaryColor = adjust(primaryColor, { h: 180, l: 5 }); this.sectionBkgColor = this.sectionBkgColor || tertiaryColor; this.altSectionBkgColor = this.altSectionBkgColor || 'white'; this.sectionBkgColor = this.sectionBkgColor || secondaryColor; this.sectionBkgColor2 = this.sectionBkgColor2 || primaryColor; this.excludeBkgColor = this.excludeBkgColor || '#eeeeee'; this.taskBorderColor = this.taskBorderColor || this.primaryBorderColor; this.taskBkgColor = this.taskBkgColor || primaryColor; this.activeTaskBorderColor = this.activeTaskBorderColor || primaryColor; this.activeTaskBkgColor = this.activeTaskBkgColor || lighten(primaryColor, 23); this.gridColor = this.gridColor || 'lightgrey'; this.doneTaskBkgColor = this.doneTaskBkgColor || 'lightgrey'; this.doneTaskBorderColor = this.doneTaskBorderColor || 'grey'; this.critBorderColor = this.critBorderColor || '#ff8888'; this.critBkgColor = this.critBkgColor || 'red'; this.todayLineColor = this.todayLineColor || 'red'; this.taskTextColor = this.taskTextColor || this.textColor; this.taskTextOutsideColor = this.taskTextOutsideColor || this.textColor; this.taskTextLightColor = this.taskTextLightColor || this.textColor; this.taskTextColor = this.taskTextColor || this.primaryTextColor; this.taskTextDarkColor = this.taskTextDarkColor || this.textColor; this.taskTextClickableColor = this.taskTextClickableColor || '#003163'; /* Architecture Diagram variables */ this.archEdgeColor = this.lineColor; this.archEdgeArrowColor = this.lineColor; /* Sequence Diagram variables */ this.personBorder = this.personBorder || this.primaryBorderColor; this.personBkg = this.personBkg || this.mainBkg; /* state colors */ this.transitionColor = this.transitionColor || this.lineColor; this.transitionLabelColor = this.transitionLabelColor || this.textColor; /* The color of the text tables of the states*/ this.stateLabelColor = this.stateLabelColor || this.stateBkg || this.primaryTextColor; this.stateBkg = this.stateBkg || this.mainBkg; this.labelBackgroundColor = this.labelBackgroundColor || this.stateBkg; this.compositeBackground = this.compositeBackground || this.background || this.tertiaryColor; this.altBackground = this.altBackground || '#f0f0f0'; this.compositeTitleBackground = this.compositeTitleBackground || this.mainBkg; this.compositeBorder = this.compositeBorder || this.nodeBorder; this.innerEndBackground = this.nodeBorder; this.errorBkgColor = this.errorBkgColor || this.tertiaryColor; this.errorTextColor = this.errorTextColor || this.tertiaryTextColor; this.transitionColor = this.transitionColor || this.lineColor; this.specialStateColor = this.lineColor; /* Color Scale */ /* Each color-set will have a background, a foreground and a border color */ this.cScale0 = this.cScale0 || primaryColor; this.cScale1 = this.cScale1 || secondaryColor; this.cScale2 = this.cScale2 || tertiaryColor; this.cScale3 = this.cScale3 || adjust(primaryColor, { h: 30 }); this.cScale4 = this.cScale4 || adjust(primaryColor, { h: 60 }); this.cScale5 = this.cScale5 || adjust(primaryColor, { h: 90 }); this.cScale6 = this.cScale6 || adjust(primaryColor, { h: 120 }); this.cScale7 = this.cScale7 || adjust(primaryColor, { h: 150 }); this.cScale8 = this.cScale8 || adjust(primaryColor, { h: 210, l: 150 }); this.cScale9 = this.cScale9 || adjust(primaryColor, { h: 270 }); this.cScale10 = this.cScale10 || adjust(primaryColor, { h: 300 }); this.cScale11 = this.cScale11 || adjust(primaryColor, { h: 330 }); if (this.darkMode) { for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) { this['cScale' + i] = darken(this['cScale' + i], 75); } } else { for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) { this['cScale' + i] = darken(this['cScale' + i], 25); } } // Setup the inverted color for the set for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) { this['cScaleInv' + i] = this['cScaleInv' + i] || invert(this['cScale' + i]); } // Setup the peer color for the set, useful for borders for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) { if (this.darkMode) { this['cScalePeer' + i] = this['cScalePeer' + i] || lighten(this['cScale' + i], 10); } else { this['cScalePeer' + i] = this['cScalePeer' + i] || darken(this['cScale' + i], 10); } } // Setup the label color for the set this.scaleLabelColor = this.scaleLabelColor || this.labelTextColor; for (let i = 0; i < this.THEME_COLOR_LIMIT; i++) { this['cScaleLabel' + i] = this['cScaleLabel' + i] || this.scaleLabelColor; } const multiplier = this.darkMode ? -4 : -1; for (let i = 0; i < 5; i++) { this['surface' + i] = this['surface' + i] || adjust(this.mainBkg, { h: 180, s: -15, l: multiplier * (5 + i * 3) }); this['surfacePeer' + i] = this['surfacePeer' + i] || adjust(this.mainBkg, { h: 180, s: -15, l: multiplier * (8 + i * 3) }); } /* class */ this.classText = this.classText || this.textColor; /* user-journey */ this.fillType0 = this.fillType0 || primaryColor; this.fillType1 = this.fillType1 || secondaryColor; this.fillType2 = this.fillType2 || adjust(primaryColor, { h: 64 }); this.fillType3 = this.fillType3 || adjust(secondaryColor, { h: 64 }); this.fillType4 = this.fillType4 || adjust(primaryColor, { h: -64 }); this.fillType5 = this.fillType5 || adjust(secondaryColor, { h: -64 }); this.fillType6 = this.fillType6 || adjust(primaryColor, { h: 128 }); this.fillType7 = this.fillType7 || adjust(secondaryColor, { h: 128 }); /* pie */ this.pie1 = this.pie1 || primaryColor; this.pie2 = this.pie2 || secondaryColor; this.pie3 = this.pie3 || tertiaryColor; this.pie4 = this.pie4 || adjust(primaryColor, { l: -10 }); this.pie5 = this.pie5 || adjust(secondaryColor, { l: -10 }); this.pie6 = this.pie6 || adjust(tertiaryColor, { l: -10 }); this.pie7 = this.pie7 || adjust(primaryColor, { h: +60, l: -10 }); this.pie8 = this.pie8 || adjust(primaryColor, { h: -60, l: -10 }); this.pie9 = this.pie9 || adjust(primaryColor, { h: 120, l: 0 }); this.pie10 = this.pie10 || adjust(primaryColor, { h: +60, l: -20 }); this.pie11 = this.pie11 || adjust(primaryColor, { h: -60, l: -20 }); this.pie12 = this.pie12 || adjust(primaryColor, { h: 120, l: -10 }); this.pieTitleTextSize = this.pieTitleTextSize || '25px'; this.pieTitleTextColor = this.pieTitleTextColor || this.taskTextDarkColor; this.pieSectionTextSize = this.pieSectionTextSize || '17px'; this.pieSectionTextColor = this.pieSectionTextColor || this.textColor; this.pieLegendTextSize = this.pieLegendTextSize || '17px'; this.pieLegendTextColor = this.pieLegendTextColor || this.taskTextDarkColor; this.pieStrokeColor = this.pieStrokeColor || 'black'; this.pieStrokeWidth = this.pieStrokeWidth || '2px'; this.pieOuterStrokeWidth = this.pieOuterStrokeWidth || '2px'; this.pieOuterStrokeColor = this.pieOuterStrokeColor || 'black'; this.pieOpacity = this.pieOpacity || '0.7'; /* quadrant-graph */ this.quadrant1Fill = this.quadrant1Fill || primaryColor; this.quadrant2Fill = this.quadrant2Fill || adjust(primaryColor, { r: 5, g: 5, b: 5 }); this.quadrant3Fill = this.quadrant3Fill || adjust(primaryColor, { r: 10, g: 10, b: 10 }); this.quadrant4Fill = this.quadrant4Fill || adjust(primaryColor, { r: 15, g: 15, b: 15 }); this.quadrant1TextFill = this.quadrant1TextFill || this.primaryTextColor; this.quadrant2TextFill = this.quadrant2TextFill || adjust(this.primaryTextColor, { r: -5, g: -5, b: -5 }); this.quadrant3TextFill = this.quadrant3TextFill || adjust(this.primaryTextColor, { r: -10, g: -10, b: -10 }); this.quadrant4TextFill = this.quadrant4TextFill || adjust(this.primaryTextColor, { r: -15, g: -15, b: -15 }); this.quadrantPointFill = this.quadrantPointFill || isDark(this.quadrant1Fill) ? lighten(this.quadrant1Fill) : darken(this.quadrant1Fill); this.quadrantPointTextFill = this.quadrantPointTextFill || this.primaryTextColor; this.quadrantXAxisTextFill = this.quadrantXAxisTextFill || this.primaryTextColor; this.quadrantYAxisTextFill = this.quadrantYAxisTextFill || this.primaryTextColor; this.quadrantInternalBorderStrokeFill = this.quadrantInternalBorderStrokeFill || this.primaryBorderColor; this.quadrantExternalBorderStrokeFill = this.quadrantExternalBorderStrokeFill || this.primaryBorderColor; this.quadrantTitleFill = this.quadrantTitleFill || this.primaryTextColor; /* xychart */ this.xyChart = { backgroundColor: this.xyChart?.backgroundColor || this.background, titleColor: this.xyChart?.titleColor || this.primaryTextColor, xAxisTitleColor: this.xyChart?.xAxisTitleColor || this.primaryTextColor, xAxisLabelColor: this.xyChart?.xAxisLabelColor || this.primaryTextColor, xAxisTickColor: this.xyChart?.xAxisTickColor || this.primaryTextColor, xAxisLineColor: this.xyChart?.xAxisLineColor || this.primaryTextColor, yAxisTitleColor: this.xyChart?.yAxisTitleColor || this.primaryTextColor, yAxisLabelColor: this.xyChart?.yAxisLabelColor || this.primaryTextColor, yAxisTickColor: this.xyChart?.yAxisTickColor || this.primaryTextColor, yAxisLineColor: this.xyChart?.yAxisLineColor || this.primaryTextColor, plotColorPalette: this.xyChart?.plotColorPalette || '#FFF4DD,#FFD8B1,#FFA07A,#ECEFF1,#D6DBDF,#C3E0A8,#FFB6A4,#FFD74D,#738FA7,#FFFFF0', }; /* requirement-diagram */ this.requirementBackground = this.requirementBackground || primaryColor; this.requirementBorderColor = this.requirementBorderColor || this.primaryBorderColor; this.requirementBorderSize = this.requirementBorderSize || '1'; this.requirementTextColor = this.requirementTextColor || this.primaryTextColor; this.relationColor = this.relationColor || this.lineColor; this.relationLabelBackground = this.relationLabelBackground || (this.darkMode ? darken(this.secondaryColor, 30) : this.secondaryColor); this.relationLabelColor = this.relationLabelColor || this.actorTextColor; /* git */ this.git0 = this.git0 || primaryColor; this.git1 = this.git1 || secondaryColor; this.git2 = this.git2 || tertiaryColor; this.git3 = this.git3 || adjust(primaryColor, { h: -30 }); this.git4 = this.git4 || adjust(primaryColor, { h: -60 }); this.git5 = this.git5 || adjust(primaryColor, { h: -90 }); this.git6 = this.git6 || adjust(primaryColor, { h: +60 }); this.git7 = this.git7 || adjust(primaryColor, { h: +120 }); if (this.darkMode) { this.git0 = lighten(this.git0, 25); this.git1 = lighten(this.git1, 25); this.git2 = lighten(this.git2, 25); this.git3 = lighten(this.git3, 25); this.git4 = lighten(this.git4, 25); this.git5 = lighten(this.git5, 25); this.git6 = lighten(this.git6, 25); this.git7 = lighten(this.git7, 25); } else { this.git0 = darken(this.git0, 25); this.git1 = darken(this.git1, 25); this.git2 = darken(this.git2, 25); this.git3 = darken(this.git3, 25); this.git4 = darken(this.git4, 25); this.git5 = darken(this.git5, 25); this.git6 = darken(this.git6, 25); this.git7 = darken(this.git7, 25); } this.gitInv0 = this.gitInv0 || invert(this.git0); this.gitInv1 = this.gitInv1 || invert(this.git1); this.gitInv2 = this.gitInv2 || invert(this.git2); this.gitInv3 = this.gitInv3 || invert(this.git3); this.gitInv4 = this.gitInv4 || invert(this.git4); this.gitInv5 = this.gitInv5 || invert(this.git5); this.gitInv6 = this.gitInv6 || invert(this.git6); this.gitInv7 = this.gitInv7 || invert(this.git7); this.branchLabelColor = this.branchLabelColor || (this.darkMode ? 'black' : this.labelTextColor); this.gitBranchLabel0 = this.gitBranchLabel0 || this.branchLabelColor; this.gitBranchLabel1 = this.gitBranchLabel1 || this.branchLabelColor; this.gitBranchLabel2 = this.gitBranchLabel2 || this.branchLabelColor; this.gitBranchLabel3 = this.gitBranchLabel3 || this.branchLabelColor; this.gitBranchLabel4 = this.gitBranchLabel4 || this.branchLabelColor; this.gitBranchLabel5 = this.gitBranchLabel5 || this.branchLabelColor; this.gitBranchLabel6 = this.gitBranchLabel6 || this.branchLabelColor; this.gitBranchLabel7 = this.gitBranchLabel7 || this.branchLabelColor; this.tagLabelColor = this.tagLabelColor || this.primaryTextColor; this.tagLabelBackground = this.tagLabelBackground || this.primaryColor; this.tagLabelBorder = this.tagBorder || this.primaryBorderColor; this.tagLabelFontSize = this.tagLabelFontSize || '10px'; this.commitLabelColor = this.commitLabelColor || this.secondaryTextColor; this.commitLabelBackground = this.commitLabelBackground || this.secondaryColor; this.commitLabelFontSize = this.commitLabelFontSize || '10px'; /* -------------------------------------------------- */ /* EntityRelationship diagrams */ this.attributeBackgroundColorOdd = this.attributeBackgroundColorOdd || oldAttributeBackgroundColorOdd; this.attributeBackgroundColorEven = this.attributeBackgroundColorEven || oldAttributeBackgroundColorEven; /* -------------------------------------------------- */ } calculate(overrides) { if (typeof overrides !== 'object') { // Calculate colors form base colors this.updateColors(); return; } const keys = Object.keys(overrides); // Copy values from overrides, this is mainly for base colors keys.forEach((k) => { this[k] = overrides[k]; }); // Calculate colors form base colors this.updateColors(); // Copy values from overrides again in case of an override of derived value keys.forEach((k) => { this[k] = overrides[k]; }); } } export const getThemeVariables = (userOverrides) => { const theme = new Theme(); theme.calculate(userOverrides); return theme; };