From f822a0da50f329e7229be7425ef77f376cb47154 Mon Sep 17 00:00:00 2001 From: Daniel <50702276+daniboygg@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:36:11 +0100 Subject: [PATCH] Gh 3793 upload color theme to server and improve color pickers (#5286) * Admin: save personalized colors * Fix buttons with incorrect cursor * Minor: format with prettier * Internal: add alternative to primary button * Fis style of primary button * Admin: Improve color picker with hexadecimal value * Format base input text and add property to change input class * Install colorjs to handle colors * Admin: Add more example elements to custom colors screen * Core: Fix dropdown to allow any kind of values * Minor: minor improvements to base components * Minor: use dev-server for enabling hot reloading * This will automatic reload the page when something changes https://symfony.com/doc/current/frontend/encore/dev-server.html * Internal: add colors from design * Use the same name for every property to be consistent * Internal: use correct colors for buttons and add missing ones * Minor: fix type definition for some base components * Internal: use new color library in base color picker * Admin: Add advanced mode to pick colors of chamilo * Admin: Use standard rgb format for colors * rgba is a legacy feature of browsers, that still works, but use standard feature will ensure better compatibility * Admin: Add contrast checker for color picker * Admin: Simple mode by default in color picker * Minor: remove not needed change in package.json --- assets/css/app.scss | 35 +- .../components/basecomponents/BaseButton.vue | 39 +- .../basecomponents/BaseColorPicker.vue | 81 ++- .../basecomponents/BaseDropdown.vue | 3 +- .../basecomponents/BaseInputDate.vue | 1 - .../basecomponents/BaseInputText.vue | 37 +- .../components/basecomponents/validators.js | 2 +- assets/vue/composables/theme.js | 27 +- .../vue/views/admin/AdminConfigureColors.vue | 558 +++++++++++++++--- package.json | 1 + tailwind.config.js | 44 +- 11 files changed, 676 insertions(+), 152 deletions(-) diff --git a/assets/css/app.scss b/assets/css/app.scss index 183fb95de4..93da3ad2d4 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -11,19 +11,36 @@ // TAILWIND COLOR DEFINITION https://tailwindcss.com/docs/customizing-colors#using-css-variables @layer base { :root { - --color-primary-base: 46, 117, 163; - --color-primary-gradient: 156, 194, 218; + --color-primary-base: 46 117 163; + --color-primary-gradient: 36 77 103; + --color-primary-button-text: 46 117 163; + --color-primary-button-alternative-text: 255 255 255; - --color-secondary-base: 243, 126, 47; - --color-secondary-gradient: 224, 100, 16; + --color-secondary-base: 243 126 47; + --color-secondary-gradient: 224 100 16; + --color-secondary-button-text: 255 255 255; - --color-tertiary-base: 0, 0, 0; - --color-tertiary-gradient: 51, 51, 51; + --color-tertiary-base: 51 51 51; + --color-tertiary-gradient: 0 0 0; + --color-tertiary-button-text: 255 255 255; - --color-success-base: 119, 170, 12; - --color-success-gradient: 84, 119, 8; + --color-success-base: 119 170 12; + --color-success-gradient: 83 127 0; + --color-success-button-text: 255 255 255; - --color-danger-base: 223, 59, 59; + --color-info-base: 13 123 253; + --color-info-gradient: 0 84 211; + --color-info-button-text: 255 255 255; + + --color-warning-base: 245 206 1; + --color-warning-gradient: 186 152 0; + --color-warning-button-text: 0 0 0; + + --color-danger-base: 223 59 59; + --color-danger-gradient: 180 0 21; + --color-danger-button-text: 255 255 255; + + --color-form-base: 46 117 163; } } diff --git a/assets/vue/components/basecomponents/BaseButton.vue b/assets/vue/components/basecomponents/BaseButton.vue index d2e6050e21..61570b5a07 100644 --- a/assets/vue/components/basecomponents/BaseButton.vue +++ b/assets/vue/components/basecomponents/BaseButton.vue @@ -1,5 +1,6 @@ @@ -31,7 +46,7 @@ const props = defineProps({ default: "", }, modelValue: { - type: String, + type: [String, null], required: true, }, errorText: { @@ -41,17 +56,23 @@ const props = defineProps({ }, isInvalid: { type: Boolean, - required: false, default: false, }, required: { type: Boolean, default: false, }, - helpText: String, + helpText: { + type: String, + default: "", + }, formSubmitted: { type: Boolean, - default: false + default: false, + }, + inputClass: { + type: String, + default: "", }, }) diff --git a/assets/vue/components/basecomponents/validators.js b/assets/vue/components/basecomponents/validators.js index 26f92aa435..a5b3df0871 100644 --- a/assets/vue/components/basecomponents/validators.js +++ b/assets/vue/components/basecomponents/validators.js @@ -21,5 +21,5 @@ export const buttonTypeValidator = (value) => { if (typeof value !== "string") { return false } - return ["primary", "secondary", "black", "success", "danger"].includes(value) + return ["primary", "primary-alternative", "secondary", "black", "success", "info", "warning", "danger"].includes(value) } diff --git a/assets/vue/composables/theme.js b/assets/vue/composables/theme.js index a883df7b78..927a238cee 100644 --- a/assets/vue/composables/theme.js +++ b/assets/vue/composables/theme.js @@ -1,4 +1,5 @@ import { onMounted, ref, watch } from "vue" +import Color from "colorjs.io" export const useTheme = () => { let colors = {} @@ -24,26 +25,36 @@ export const useTheme = () => { } const getCssVariableValue = (variableName) => { - const colorVariable = getComputedStyle(document.body).getPropertyValue(variableName).split(", ") - return { - r: parseInt(colorVariable[0]), - g: parseInt(colorVariable[1]), - b: parseInt(colorVariable[2]), - } + const colorVariable = getComputedStyle(document.body).getPropertyValue(variableName) + return colorFromCSSVariable(colorVariable) } const setCssVariableValue = (variableName, color) => { - document.documentElement.style.setProperty(variableName, `${color.r}, ${color.g}, ${color.b}`) + document.documentElement.style.setProperty(variableName, colorToCSSVariable(color)) } const getColors = () => { let colorsPlainObject = {} for (const [key, value] of Object.entries(colors)) { - colorsPlainObject[key] = `${value.value.r}, ${value.value.g}, ${value.value.b}` + colorsPlainObject[key] = colorToCSSVariable(value.value) } return colorsPlainObject } + const colorToCSSVariable = (color) => { + // according to documentation https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb#syntax + // the format "r g b" should work but because how rules in css are defined does not + // so, we need the format "r, g, b" for variables, + const r = Math.round(color.r * 255) + const g = Math.round(color.g * 255) + const b = Math.round(color.b * 255) + return `${r} ${g} ${b}` + } + + const colorFromCSSVariable = (variable) => { + return new Color(`rgb(${variable})`) + } + return { getColorTheme, getColors, diff --git a/assets/vue/views/admin/AdminConfigureColors.vue b/assets/vue/views/admin/AdminConfigureColors.vue index 6ba384cd6a..404849a730 100644 --- a/assets/vue/views/admin/AdminConfigureColors.vue +++ b/assets/vue/views/admin/AdminConfigureColors.vue @@ -1,57 +1,180 @@ diff --git a/package.json b/package.json index 6a714146a2..a6bf68b681 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "bootstrap-daterangepicker": "^3.1.0", "bootstrap-select": "^1.13.18", "chart.js": "^3.9.1", + "colorjs.io": "^0.5.0", "cropper": "^4.1.0", "datepair.js": "^0.4.17", "dotenv": "^16.3.1", diff --git a/tailwind.config.js b/tailwind.config.js index eab3bf9c1a..d346d36494 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,10 +1,12 @@ -const colors = require("tailwindcss/colors"); +const colors = require("tailwindcss/colors") // from tailwind youtube channel https://youtu.be/MAtaT8BZEAo?t=1023 const colorWithOpacity = (variableName) => { - return ({opacityValue}) => { + return ({ opacityValue }) => { if (opacityValue !== undefined) { - return `rgba(var(${variableName}), ${opacityValue})` + // use standard syntax rgb (color / alpha) rgba is legacy + // check https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb + return `rgb(var(${variableName}) / ${opacityValue})` } return `rgb(var(${variableName}))` } @@ -24,16 +26,20 @@ module.exports = { gradient: colorWithOpacity("--color-primary-gradient"), bgdisabled: "#fafafa", borderdisabled: "#e4e9ed", + "button-text": colorWithOpacity("--color-primary-button-text"), + "button-alternative-text": colorWithOpacity("--color-primary-button-alternative-text"), }, secondary: { DEFAULT: colorWithOpacity("--color-secondary-base"), gradient: colorWithOpacity("--color-secondary-gradient"), - bgdisabled: '#e4e9ed', + bgdisabled: "#e4e9ed", hover: "#d35e0f", + "button-text": colorWithOpacity("--color-secondary-button-text"), }, tertiary: { DEFAULT: colorWithOpacity("--color-tertiary-base"), gradient: colorWithOpacity("--color-tertiary-gradient"), + "button-text": colorWithOpacity("--color-tertiary-button-text"), }, gray: { 5: "rgba(250, 250, 250, 0.5)", @@ -53,20 +59,37 @@ module.exports = { 5: "#e06410", 6: "#faf7f5", }, - warning: "#f5ce01", success: { DEFAULT: colorWithOpacity("--color-success-base"), gradient: colorWithOpacity("--color-success-gradient"), + "button-text": colorWithOpacity("--color-success-button-text"), }, + info: { + DEFAULT: colorWithOpacity("--color-info-base"), + gradient: colorWithOpacity("--color-info-gradient"), + "button-text": colorWithOpacity("--color-info-button-text"), + }, + warning: { + DEFAULT: colorWithOpacity("--color-warning-base"), + gradient: colorWithOpacity("--color-warning-gradient"), + "button-text": colorWithOpacity("--color-warning-button-text"), + }, + danger: { + DEFAULT: colorWithOpacity("--color-danger-base"), + gradient: colorWithOpacity("--color-danger-gradient"), + "button-text": colorWithOpacity("--color-danger-button-text"), + }, + // error is used in some places in css to this is the same color as danger DEFAULT error: colorWithOpacity("--color-danger-base"), - info: "#0d7bfd", + + form: colorWithOpacity("--color-form-base"), white: colors.white, black: colors.black, transparent: colors.transparent, current: colors.current, - fontdisabled: '#a2a6b0', + fontdisabled: "#a2a6b0", }, extend: { fontFamily: { @@ -83,8 +106,5 @@ module.exports = { corePlugins: { aspectRatio: true, }, - plugins: [ - require("@tailwindcss/forms"), - require("@tailwindcss/typography"), - ], -}; + plugins: [require("@tailwindcss/forms"), require("@tailwindcss/typography")], +}