import { Emitter, Event } from '@opensumi/ide-core-common'; import { Color } from './color'; import { ColorContribution, ColorDefaults, ColorFunction, ColorIdentifier, ColorValue, ITheme } from './theme.service'; // color registry export const Extensions = { ColorContribution: 'base.contributions.colors', }; export interface IColorRegistry { /** * Register a color to the registry. * @param id The color id as used in theme description files * @param defaults The default values * @description the description */ registerColor(id: string, defaults: ColorDefaults, description: string): ColorIdentifier; /** * Register a color to the registry. */ deregisterColor(id: string): void; /** * Get all color contributions */ getColors(): ColorContribution[]; /** * Get color contributions by id */ getColor(id: string): ColorContribution; /** * Gets the default color of the given id */ resolveDefaultColor(id: ColorIdentifier, theme: ITheme): Color | undefined; onDidColorChangedEvent: Event; } class ColorRegistry implements IColorRegistry { private colorsById: { [key: string]: ColorContribution }; private onDidColorChanged: Emitter = new Emitter(); onDidColorChangedEvent: Event = Event.debounce(this.onDidColorChanged.event, () => {}, 500); constructor() { this.colorsById = {}; } public registerColor( id: string, defaults: ColorDefaults | null, description: string, needsTransparency = false, deprecationMessage?: string, ): ColorIdentifier { const colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage }; this.colorsById[id] = colorContribution; this.onDidColorChanged.fire(); return id; } public deregisterColor(id: string): void { delete this.colorsById[id]; } public getColors(): ColorContribution[] { return Object.keys(this.colorsById).map((id) => this.colorsById[id]); } public getColor(id: string): ColorContribution { return this.colorsById[id]; } public resolveDefaultColor(id: ColorIdentifier, theme: ITheme): Color | undefined { const colorDesc = this.colorsById[id]; if (colorDesc && colorDesc.defaults) { const colorValue = colorDesc.defaults[theme.type]; return resolveColorValue(colorValue, theme); } return undefined; } public toString() { const sorter = (a: string, b: string) => { const cat1 = a.indexOf('.') === -1 ? 0 : 1; const cat2 = b.indexOf('.') === -1 ? 0 : 1; if (cat1 !== cat2) { return cat1 - cat2; } return a.localeCompare(b); }; return Object.keys(this.colorsById) .sort(sorter) .map((k) => `- \`${k}\`: ${this.colorsById[k].description}`) .join('\n'); } } const colorRegistry = new ColorRegistry(); export function getColorRegistry(): IColorRegistry { return colorRegistry; } export function registerColor( id: string, defaults: ColorDefaults | null, description: string, needsTransparency?: boolean, deprecationMessage?: string, ): ColorIdentifier { return colorRegistry.registerColor(id, defaults, description, needsTransparency, deprecationMessage); } export function darken(colorValue: ColorValue, factor: number): ColorFunction { return (theme) => { const color = resolveColorValue(colorValue, theme); if (color) { return color.darken(factor); } return undefined; }; } export function lighten(colorValue: ColorValue, factor: number): ColorFunction { return (theme) => { const color = resolveColorValue(colorValue, theme); if (color) { return color.lighten(factor); } return undefined; }; } export function transparent(colorValue: ColorValue, factor: number): ColorFunction { return (theme) => { const color = resolveColorValue(colorValue, theme); if (color) { return color.transparent(factor); } return undefined; }; } export function opaque(colorValue: ColorValue, background: ColorValue): ColorFunction { return (theme) => { const backgroundColor = resolveColorValue(background, theme); if (!backgroundColor) { return resolveColorValue(colorValue, theme); } return resolveColorValue(colorValue, theme)?.makeOpaque(backgroundColor); }; } export function oneOf(...colorValues: ColorValue[]): ColorFunction { return (theme) => { for (const colorValue of colorValues) { const color = resolveColorValue(colorValue, theme); if (color) { return color; } } return undefined; }; } export function lessProminent( colorValue: ColorValue, backgroundColorValue: ColorValue, factor: number, transparency: number, ): ColorFunction { return (theme) => { const from = resolveColorValue(colorValue, theme); if (from) { const backgroundColor = resolveColorValue(backgroundColorValue, theme); if (backgroundColor) { if (from.isDarkerThan(backgroundColor)) { return Color.getLighterColor(from, backgroundColor, factor).transparent(transparency); } return Color.getDarkerColor(from, backgroundColor, factor).transparent(transparency); } return from.transparent(factor * transparency); } return undefined; }; } /** * @param colorValue Resolve a color value in the context of a theme */ export function resolveColorValue(colorValue: ColorValue | null, theme: ITheme): Color | undefined { if (colorValue === null) { return undefined; } else if (colorValue === 'transparent') { return Color.fromHex('#00000000'); } else if (typeof colorValue === 'string') { if (colorValue[0] === '#') { return Color.fromHex(colorValue); } return theme.getColor(colorValue); } else if (colorValue instanceof Color) { return colorValue; } else if (typeof colorValue === 'function') { return colorValue(theme) as Color; } return undefined; }