From 41c84559e1060b9cca450f623c32f100e0ee87e7 Mon Sep 17 00:00:00 2001 From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com> Date: Tue, 16 Jul 2024 13:38:35 -0500 Subject: [PATCH] Admin: Allow to edit and set the current theme while changing the selected one --- assets/css/scss/_admin_index.scss | 30 +- .../vue/components/admin/ColorThemeForm.vue | 433 ++++++++++++++++++ .../platform/ColorThemeSelector.vue | 13 +- assets/vue/composables/theme.js | 65 ++- assets/vue/services/colorThemeService.js | 30 +- .../vue/views/admin/AdminConfigureColors.vue | 426 +---------------- src/CoreBundle/Entity/ColorTheme.php | 2 +- 7 files changed, 537 insertions(+), 462 deletions(-) create mode 100644 assets/vue/components/admin/ColorThemeForm.vue diff --git a/assets/css/scss/_admin_index.scss b/assets/css/scss/_admin_index.scss index 881ffb8bed..0fe761b5e6 100644 --- a/assets/css/scss/_admin_index.scss +++ b/assets/css/scss/_admin_index.scss @@ -13,30 +13,18 @@ .admin-colors { &__container { - @apply flex flex-col mt-6; + @apply flex flex-col md:flex-row mt-6; } - &__settings { - @apply flex flex-col gap-4 - md:flex-row; - - &-form { - @apply flex-1; - - &-title { - @apply mb-4; - } - } + &__form { + @apply w-full md:w-3/5; + } - .row-group { - @apply flex flex-col gap-4 items-start - sm:grid sm:grid-cols-2 - md:items-end - xl:grid-cols-3; - } + &__form-fields { + @apply mt-4; + } - &-preview { - @apply space-y-4 flex-1; - } + &__preview { + @apply flex w-full md:w-2/5; } } diff --git a/assets/vue/components/admin/ColorThemeForm.vue b/assets/vue/components/admin/ColorThemeForm.vue new file mode 100644 index 0000000000..db2fd29704 --- /dev/null +++ b/assets/vue/components/admin/ColorThemeForm.vue @@ -0,0 +1,433 @@ + + + diff --git a/assets/vue/components/platform/ColorThemeSelector.vue b/assets/vue/components/platform/ColorThemeSelector.vue index b99c2e5f4b..0036af10d8 100644 --- a/assets/vue/components/platform/ColorThemeSelector.vue +++ b/assets/vue/components/platform/ColorThemeSelector.vue @@ -1,6 +1,6 @@ diff --git a/assets/vue/composables/theme.js b/assets/vue/composables/theme.js index dc934a1f75..c2c2f82ece 100644 --- a/assets/vue/composables/theme.js +++ b/assets/vue/composables/theme.js @@ -1,8 +1,11 @@ import { onMounted, ref, watch } from "vue" +import { useI18n } from "vue-i18n" import Color from "colorjs.io" import { usePlatformConfig } from "../store/platformConfig" export const useTheme = () => { + const { t } = useI18n() + let colors = {} onMounted(() => { @@ -27,7 +30,7 @@ export const useTheme = () => { const getCssVariableValue = (variableName) => { const colorVariable = getComputedStyle(document.body).getPropertyValue(variableName) - return colorFromCSSVariable(colorVariable) + return getColorFromCSSVariable(colorVariable) } const setCssVariableValue = (variableName, color) => { @@ -48,7 +51,7 @@ export const useTheme = () => { console.error(`Color with key ${key} is on color set`) continue } - colors[key].value = colorFromCSSVariable(colorsObj[key]) + colors[key].value = getColorFromCSSVariable(colorsObj[key]) } } @@ -62,28 +65,78 @@ export const useTheme = () => { return `${r} ${g} ${b}` } - const colorFromCSSVariable = (variable) => { + const getColorFromCSSVariable = (variable) => { return new Color(`rgb(${variable})`) } + /** + * @param {Color} color + * @returns {Color} + */ + function makeGradient(color) { + const light = color.clone().to("oklab").l + // when color is light (lightness > 0.5), darken gradient color + // when color is dark, lighten gradient color + // The values 0.5 and 1.6 were chosen through experimentation, there could be a better way to do this + if (light > 0.5) { + return color + .clone() + .set({ "oklab.l": (l) => l * 0.8 }) + .to("srgb") + } + + return color + .clone() + .set({ "oklab.l": (l) => l * 1.6 }) + .to("srgb") + } + + /** + * @param {Color} color + * @returns {Color} + */ + function makeTextWithContrast(color) { + // according to colorjs library https://colorjs.io/docs/contrast#accessible-perceptual-contrast-algorithm-apca + // this algorithm is better than WCAGG 2.1 to check for contrast + // "APCA is being evaluated for use in version 3 of the W3C Web Content Accessibility Guidelines (WCAG)" + let onWhite = Math.abs(color.contrast("white", "APCA")) + let onBlack = Math.abs(color.contrast("black", "APCA")) + + return onWhite > onBlack ? new Color("white") : new Color("black") + } + + function checkColorContrast(background, foreground) { + // using APCA for text contrast in buttons. In chamilo buttons the text + // has a font size of 16px and weight of 600 + // Lc 60 The minimum level recommended for content text that is not body, column, or block text + // https://git.apcacontrast.com/documentation/APCA_in_a_Nutshell#use-case--size-ranges + let contrast = Math.abs(background.contrast(foreground, "APCA")) + + if (contrast < 60) { + return t("Does not have enough contrast against background") + } + + return "" + } + return { getColorTheme, getColors, setColors, + makeGradient, + makeTextWithContrast, + checkColorContrast, } } export function useVisualTheme() { const platformConfigStore = usePlatformConfig() - const themeName = platformConfigStore.visualTheme - function getThemeAssetUrl(path) { return `/themes/${platformConfigStore.visualTheme}/${path}` } return { - themeName, getThemeAssetUrl, } } diff --git a/assets/vue/services/colorThemeService.js b/assets/vue/services/colorThemeService.js index d1e86a6581..1a2aed9a57 100644 --- a/assets/vue/services/colorThemeService.js +++ b/assets/vue/services/colorThemeService.js @@ -12,27 +12,34 @@ async function findAllByCurrentUrl() { } /** - * Update or create a theme with the title + * Create a color theme * - * @param {string|null} iri * @param {string} title * @param {Object} colors * @returns {Promise} */ -async function updateTheme({ iri = null, title, colors }) { - if (iri) { - return await baseService.put(iri, { - title, - variables: colors, - }) - } - +async function create({ title, colors }) { return await baseService.post(url, { title, variables: colors, }) } +/** + * Update a color theme + * + * @param {string} iri + * @param {string} title + * @param {Object} colors + * @returns {Promise} + */ +async function update({ iri, title, colors }) { + return await baseService.put(iri, { + title, + variables: colors, + }) +} + /** * @param {string} iri * @returns {Promise} @@ -44,7 +51,8 @@ async function changePlatformColorTheme(iri) { } export default { - updateTheme, + create, + update, findAllByCurrentUrl, changePlatformColorTheme, } diff --git a/assets/vue/views/admin/AdminConfigureColors.vue b/assets/vue/views/admin/AdminConfigureColors.vue index 08b4bbac41..63425ddc61 100644 --- a/assets/vue/views/admin/AdminConfigureColors.vue +++ b/assets/vue/views/admin/AdminConfigureColors.vue @@ -3,214 +3,11 @@
-
- - - - - - -
-
-

- - - - -
-
- - - - -
- -
- - - -
- -
- - -
- -
- - - -
- -
- - - -
- -
- - - -
- -
- - -
- -
- -
-
- - -
-
- - - -
- -
- - - - -
- -
- -
-
- -
- -
- -
- -
-

+
+ +
+
@@ -220,224 +17,11 @@ diff --git a/src/CoreBundle/Entity/ColorTheme.php b/src/CoreBundle/Entity/ColorTheme.php index ce30fecab1..a55306d119 100644 --- a/src/CoreBundle/Entity/ColorTheme.php +++ b/src/CoreBundle/Entity/ColorTheme.php @@ -46,7 +46,7 @@ class ColorTheme /** * @var array */ - #[Groups(['color_theme:write'])] + #[Groups(['color_theme:write', 'access_url_rel_color_theme:read'])] #[ORM\Column] private array $variables = [];