aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/design-system/src/helpers/theme.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/design-system/src/helpers/theme.ts')
-rw-r--r--server/sonar-web/design-system/src/helpers/theme.ts130
1 files changed, 130 insertions, 0 deletions
diff --git a/server/sonar-web/design-system/src/helpers/theme.ts b/server/sonar-web/design-system/src/helpers/theme.ts
new file mode 100644
index 00000000000..6dab879cf4a
--- /dev/null
+++ b/server/sonar-web/design-system/src/helpers/theme.ts
@@ -0,0 +1,130 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { CSSColor, Theme, ThemeColors, ThemeContrasts, ThemedProps } from '../types/theme';
+import { getRGBAString } from './colors';
+
+export function getProp<T>(name: keyof Omit<T, keyof ThemedProps>) {
+ return (props: T) => props[name];
+}
+
+export function themeColor(name: ThemeColors | CSSColor, opacity?: number) {
+ return function ({ theme }: ThemedProps) {
+ return getColor(theme, [], name, opacity);
+ };
+}
+
+export function themeContrast(name: ThemeColors | CSSColor) {
+ return function ({ theme }: ThemedProps) {
+ return getContrast(theme, name);
+ };
+}
+
+export function themeBorder(
+ name: keyof Theme['borders'] = 'default',
+ color?: ThemeColors | CSSColor,
+ opacity?: number
+) {
+ return function ({ theme }: ThemedProps) {
+ const [width, style, ...rgba] = theme.borders[name];
+ return `${width} ${style} ${getColor(theme, rgba as number[], color, opacity)}`;
+ };
+}
+
+export function themeShadow(
+ name: keyof Theme['shadows'],
+ color?: ThemeColors | CSSColor,
+ opacity?: number
+) {
+ return function ({ theme }: ThemedProps) {
+ const shadows = theme.shadows[name];
+ return shadows
+ .map((item) => {
+ const [x, y, blur, spread, ...rgba] = item;
+ return `${x}px ${y}px ${blur}px ${spread}px ${getColor(theme, rgba, color, opacity)}`;
+ })
+ .join(',');
+ };
+}
+
+export function themeAvatarColor(name: string, contrast = false) {
+ return function ({ theme }: ThemedProps) {
+ let hash = 0;
+ for (let i = 0; i < name.length; i++) {
+ hash = name.charCodeAt(i) + ((hash << 5) - hash);
+ }
+
+ // Reduces number length to avoid modulo's limit.
+ hash = parseInt(hash.toString().slice(-5), 10);
+ if (contrast) {
+ return getColor(theme, theme.avatar.contrast[hash % theme.avatar.contrast.length]);
+ }
+ return getColor(theme, theme.avatar.color[hash % theme.avatar.color.length]);
+ };
+}
+
+export function themeImage(imageKey: keyof Theme['images']) {
+ return function ({ theme }: ThemedProps) {
+ return theme.images[imageKey];
+ };
+}
+
+function getColor(
+ theme: Theme,
+ [r, g, b, a]: number[],
+ colorOverride?: ThemeColors | CSSColor,
+ opacityOverride?: number
+) {
+ // Custom CSS property or rgb(a) color, return it directly
+ if (
+ colorOverride?.startsWith('var(--') ||
+ colorOverride?.startsWith('rgb(') ||
+ colorOverride?.startsWith('rgba(')
+ ) {
+ return colorOverride as CSSColor;
+ }
+ // Is theme color overridden by a color name ?
+ const color = colorOverride ? theme.colors[colorOverride as ThemeColors] : [r, g, b];
+ if (typeof color === 'string') {
+ return color as CSSColor;
+ }
+
+ return getRGBAString(color, opacityOverride ?? color[3] ?? a);
+}
+
+// Simplified version of getColor for contrast colors, fallback to colors if contrast isn't found
+function getContrast(theme: Theme, colorOverride: ThemeContrasts | ThemeColors | CSSColor) {
+ // Custom CSS property or rgb(a) color, return it directly
+ if (
+ colorOverride?.startsWith('var(--') ||
+ colorOverride?.startsWith('rgb(') ||
+ colorOverride?.startsWith('rgba(')
+ ) {
+ return colorOverride as CSSColor;
+ }
+
+ // For contrast we always require a color override (it's the principle of a contrast)
+ const color =
+ theme.contrasts[colorOverride as ThemeContrasts] || theme.colors[colorOverride as ThemeColors];
+ if (typeof color === 'string') {
+ return color as CSSColor;
+ }
+
+ return getRGBAString(color, color[3]);
+}